If I have to test a service that uses a mutable entity I would build the smallest object that I need (a real one) and pass it to my service. Example:
User joe = new User();
joe.setEmail("[email protected]");
resetPasswordService.resetPassword(joe);
verif(emailServiceMock).sendEmail("[email protected]", "Your password has been reset!");
Obviously User has lots of fields but I do not set them since resetPasswordService
does not need them. This is very refactor-friendly since if I rename a User field that is not the email this test will not be changed.
The problem appears when I try to do the same with an Immutables object. I will stick with the same example and turn User from an entity into an immutable.
@Value.Immutable
public abstract class User {
public abstract String getEmail();
public abstract PostalAddress getPostalAddress();
//more fields
}
User joe = new ImmutableUserBuilder().email("[email protected]").build();
resetPasswordService.resetPassword(joe);
verif(emailServiceMock).sendEmail("[email protected]", "Your password has been reset!");
java.lang.IllegalStateException: Cannot build User, some of required attributes are not set [postalAddress, signupDate, city, ....]
This fails in the builder when it tries to build the object. So what should I do?
@Nullable
and have the builder not validate the object? This would expose me to the risk of having incomplete objects in production, right?I know Users should be entities and not immutable value objects. I used User in this example since it is easy to understand.
Simple answer: you only use mocks if you have to.
Meaning: when you need to either control the behavior of an object in ways that the "real" class doesn't support. Or when you have to verify calls on the mock.
So: when you can write a test case that does what you want it to do without using mocking - then go for that.
Mock frameworks are tools. You don't use them because you can, but because they solve a problem for you that you otherwise can't address (easily).
Beyond that: as explained, the default should be to avoid mocks. On the other hand, programming is always about balancing efforts and "return on investment". That is why I used the word easily above. When it turns out that using a mock results in writing down 2, 3 easy-to-comprehend lines of code ... but using "the real" class is much more complicated (or relies on certain implicit assumption about how that class works) - then using a mock can be the better choice.
In that sense, the answer is: don't take answers and rules as golden standard. In the end, this is always about human judgement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With