Mockito: how to mock an object that has certain property value - java

Assume we have the following class:
class Person {
private int age;
private String name;
public Person(int age, String name){
this.age = age;
this.name = name;
}
// getters and setters
}
and we also have some class:
class SpecialClass {
public int giveNumber(Person p) {
...
return (int)(...)
}
}
Assume I want to mock an object of SpecialClass that if 'giveNumber' is invoked with a Person object that has name property equals to 'John', then 'giveNumber' will retrieve 500.
For example,
SpecialClass sc = mock(SpecialClass.class);
when(sc.giveNumber(p with name = "John").thenReturn(500);
Is there any way to do it with Mockito?

You can use org.mockito.ArgumentMatchers.argThat(...) passing it a lambda that matches the desired instance. In this case the lamdba would be something like
(person) -> "John".equals(person.getName())
Putting it together:
SpecialClass sc = mock(SpecialClass.class);
when(sc.giveNumber(argThat((person) -> "John".equals(person.getName())))).thenReturn(500);

Related

Is it an anti-pattern to use Jackson's `#JsonCreator` on empty constructors?

I repeatedly saw code like the following, where the empty constructor was deleted by well-intentioned developers (because it appeared unused), which then broke Jackson serialization (caught by tests later on):
public class Person {
private String name;
private int age;
// appears unused – only used by Jackson
public Person() {}
// used in code
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
I'm thinking of adding a #JsonCreator annotation to the empty constructor to make it more obvious that the constructor is used by Jackson to prevent developers from removing it:
public class Person {
private String name;
private int age;
#JsonCreator // <--------
public Person() {}
// used in code
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Is this a good idea or does this have unintended consequences (i. e. changing semantics compared to the no-annotation case)?
Or is this use of #JsonCreator an anti-pattern and there is a better way to achieve this (short of converting the class to a record)?
Reading more documentation about this I and regarding all the comments to the question and other answer(s) I came to the conclusion that it really is an anti-pattern to use the #JsonCreator for the default constructor.
The Javadoc (e.g. from version 2.13) specifies that a ctor annotated with that annotation should have at least one parameter. (It doesn't explicitly say so, but a ctor without arguments would be useless more or less.)
NOTE: when annotating creator methods (constructors, factory methods), method must either be:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
So for your problem I would suggest that you get rid of the default ctor and instead annotate at least one of the remaining ctor(s) used in code as well properly. Jackson can use that and the IDE will not mark it as unused.
It also could save you from workarounds for not properly or completely initialized objects.
In your example this could be
public class Person {
private String name;
private int age;
#JsonCreator
public Person(#JsonProperty("name") String name, #JsonProperty("age") int age) {
this.name = name;
this.age = age;
}
// other getters / setters / business logic come here
}
It's an anti pattern, because the default constructor is the #JsonCreator default and everyone knows it - it is useless code.
You don't need to prevent developers from removing it because your functional tests will fail if they do so.

How to pass in the parameter from one class to another in Java?

Forgive me if this is a duplicated question but I'm a beginner in Java and I'm currently trying to get the parameter value from a class called Name and pass that value into another class called Student.
Name Class:
public class Name
{
public String studentName;
public Name(String fullName)
{
studentName = fullName;
}
}
Student Class:
public class Student
{
private Name studentName;
private String id;
private int credits;
public Student(String studentID)
{
studentName = new Name("");
id = studentID;
credits = 0;
}
}
What I want to do is to get the parameter value of fullName which is set in the Name class and pass it in studentName = new Name(""); for the Student class instead passing in an empty string to retrieve the name.
What do you mean by "taking the parameter value of fullName, which is set in Name class"? A class will not have a value, you need access to the instance of this class. I'm pretty sure you will have some kind of control class, e.g. where your main() resides.
At some point you will have created a Name instance:
Name n = new Name("Brandon");
Using this instance of Name class, you can access the actual value:
Student s = new Student("4711", n.studentName);
, assuming you also have included an additional parameter in your Student constructor:
public class Student
{
private Name studentName;
private String id;
private int credits;
public Student(String studentID, String name)
{
studentName = new Name(name);
id = studentID;
credits = 0;
}
}
, but this would result in you having 2 different Name objects.
Another option is to pass the object itself as parameter, so both of your objects reference to same object. Changing studentName of either n.studentName and s.studentName would in theoretically result in the value of the respective other being changed as well (I can recall some discussions regarding that topic in Java though).
public class Student
{
private Name studentName;
private String id;
private int credits;
public Student(String studentID, Name nameObject)
{
studentName = nameObject;
id = studentID;
credits = 0;
}
}
, which is instantiated by
Name n = new Name("Brandon");
Student s = new Student("4711", n);
You should definitely start reading introductions into object oriented programming, as there are quite a lot of misassumptions just in those few lines of code. The difference between class and object is crucial, also it's usual in those scenarios to have getters/setters rather than public variables in classes. To achieve the kind of dependency you want to have, you might want to look into composition and aggregation in the context of object-orientation. Also the difference between pass-by-value and pass-by-reference is worth looking into.

Lombok's builder with mandatory parameters

If I add #Builder to a class. The builder method is created.
Person.builder().name("john").surname("Smith").build();
I have a requirement where a particular field is mandatory. In this case, the name field is mandatory. Ideally, I would like to declare it like so.
Person.builder("john").surname("Smith").build();
When googling i found many alternatives like overriding the builder implementation as below:
#Builder
public class Person {
private String name;
private String surname;
public static PersonBuilder builder(String name) {
return new PersonBuilder().name(name);
}
}
And then use it like below:
Person p = Person.builder("Name").surname("Surname").build();
The problem with above approach is that it still provides the name() and PersonBuilder() method like below, which i don't want:
Person p = Person.builder("Name").surname("Surname").name("").build();
Person p = new Person.PersonBuilder().build;
Another approach is to add #lombok.nonnull check at name which will force to provide value for name while creating object. but it is a runtime check. it will not force me to provide value for name while creating object.
Is there any additional technique which lombok provides to achieve below:
Person p = Person.builder("Name").surname("Surname").build();
Note: The builder() and name() should not be exposed. The only way to create Person object should be either above or below:
Person p = Person.builder("Name").build();
You can't really do it with lombok, see the explanation from the library authors. But is it that complicated to roll this builder on your own?
public static class PersonBuilder {
private final String name;
private String surname;
PersonBuilder(String name) {
this.name = name;
}
public PersonBuilder surname(String surname) {
this.surname = surname;
return this;
}
public Person build() {
return new Person(name, surname);
}
}
with the same method that you already have:
public static PersonBuilder builder(String name) {
return new PersonBuilder(name);
}
Try to make the builder private.
Did you check this comment Required arguments with a Lombok #Builder
I am pretty sure you will find out once read the thread one more time.
P.S. If you have a class with only two field better use directly a constructor.
Best Practice:
import lombok.Builder;
import lombok.NonNull;
#Builder(builderMethodName = "privateBuilder")
public class Person {
#NonNull
private String name;
private String surname;
public static class PersonNameBuilder {
public PersonBuilder name(String name) {
return Person.privateBuilder().name(name);
}
}
private static class PersonExtraBuilder extends PersonBuilder{
#Deprecated
#Override
public PersonBuilder name(String name) {
return this;
}
}
public static PersonNameBuilder builder(String name) {
return new PersonNameBuilder();
}
private static PersonExtraBuilder privateBuilder(){
return new PersonExtraBuilder();
}
}
Usage:
PersonNameBuilder nameBuilder = Person.builder();
PersonBuilder builder = nameBuilder.name("John");
Person p1 = builder.surname("Smith").build();
// Or
Person p2 = Person.builder().name("John").surname("Smith").build();
// The last `.name("")` will not work, and it will be marked as Deprecated by IDE.
Person p3 = Person.builder().name("John").surname("Smith").name("").build();

Creating objects for each element

I'd like to do something like this:
create a class with the following properties: String name; int atomicNumber; String symbol; double mass;
create an initializer so you can just do new Element("Hydrogen", 1, "H", 1.0079) and a toString method.
now create an object for each element. note that now, you only have one line of code per element. you can store these in an array (or a dictionary that uses the name as the key).
when the user inputs the name, you can just look for it in the array (or just get the value from the key in the dictionary) and call toString on the object.
why this way? imagine that now you have to add a line in your output with the number of valence electrons for each atom. this would require a line or two in toString, rather than one line for each element.
How would I go about doing this? What would the class with the properties look like?
I tried making one:
public class Structure {
String name;
int age;
String color;
public void add(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
}
But I'm not quite sure how to go on, and Structure.add("Adele",25,"Grey"); works but doing it with another overwrites the data.
You'll want to make a new object for each element. So the class's "add" method, shouldn't be called "add". Since it won't be adding anything. Instead, let's make that a constructor. Like this:
public Structure(String name, int age, String color) {
//The inside stays the same
}
Now we can make objects like this:
Structure s = new Structure("matthew", 22, "BLUE");
We can store those objects in an array like this:
Structure[] structures = new Structures[numStructures];
for(int i = 0; i < numStructures; i++) {
structures[i] = new Structure("whatever", 99, "some color")l
}
You're supposed to store multiple Structure objects in a list.
public class Structure {
String name;
int age;
String color;
public Structure(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
}
Then, in the place you are creating objects.
List<Structure> list = new ArrayList<>();
list.add(new Structure("john",25,"blue"));
list.add(new Structure("sally",15,"green"));
list.add(new Structure("mark",35,"red"));
The point of a class is that you can create new objects of that classtype. In Java you create instances of a class like so:
Structure instance1 = new Structure();
In your case, you have an add method, so you could call that on your new object to set its properties:
instance1.add("Adele", 25, "Grey");
but it would make more sense to define a constructor:
public Structure(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
I would suggest reading up on some basic object-oriented programming concepts.
Your "first try" should be
public class Structure {
String name;
int age;
String color;
public Structure(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
}
Instead of writing a method add, you create a constructor: no return type (remove the void), and must use the same name as the class name.
For your question, use the properties name, atomicNumber, symbol and mass instead of the properties name, age and color as in your example. You must also use the class name Element instead of Structure.

Jackson library: custom mapping of a Java object in a JSON element

I have two class
public class Person {
private long id;
private String name;
private Gender gender;
// getter and setter omitted
}
and
public class Gender {
private long id;
private String value;
// getter and setter omitted
}
By default the JSON mapping with Jackson library of a Person object is:
{
id: 11,
name: "myname",
gender: {
id: 2,
value: "F"
}
}
I'd like to known how to configure Jackson to obtain:
{
id: 11,
name: "myname",
gender: "F"
}
I don't want mapping all the Gender object but only its value property.
You can use a custom serializer:
public class GenderSerializer extends JsonSerializer<Gender> {
public GenderSerializer() {
}
#Override
public void serialize(Gender gender, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(gender.getValue());
}
}
And in your Person class:
public class Person {
private long id;
private String name;
#JsonSerialize(using = GenderSerializer.class)
private Gender gender;
}
you might wanna see this for custom mapping OR if you need a short cut then you can change getter/setter of gender like this
public String getGender(String type){
this.getGender().getValue();
}
public void setGender(String value){
Gender gender = new Gender();
gender.setId(2);
gender.setValue(value);
this.gender = gender;
}
further you can also put condition for setting male/female on value= M/F
No need for custom serializer/deserializer if you can modify Gender class. For serialization you can use #JsonValue, for deserialization simple constructor (optionally annotated with #JsonCreator):
public class Gender {
#JsonCreator // optional
public Gender(String desc) {
// handle detection of "M" or "F"
}
#JsonValue
public String asString() { // name doesn't matter
if (...) return "M";
return "F";
}
}
Alternatively you could use a static factory method instead of constructor; if so, it must be annotated with #JsonCreator.
And return type of method annotated with #JsonValue can be anything that Jackson knows how to serialize; here we use String, but Enum, POJO and such are fine as well.

Categories

Resources