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("joe@example.com");
resetPasswordService.resetPassword(joe);
verif(emailServiceMock).sendEmail("joe@example.com", "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("joe@example.com").build();
resetPasswordService.resetPassword(joe);
verif(emailServiceMock).sendEmail("joe@example.com", "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?
- Use a mock for User and have it return mocks even if every time a mock returns a mock a fairy dies
- Create a testing DSL and have some sort of factory to build the entire User tree structure with all the fields I don't need? Seems heavy and not so refactor-friendly. This makes the requirements of the test not so transparent.
- Make all the fields in User
@Nullable
and have the builder not validate the object? This would expose me to the risk of having incomplete objects in production, right? - some other option I missed?
I know Users should be entities and not immutable value objects. I used User in this example since it is easy to understand.