Why do we not mock domain objects in unit tests?

Captain :

I give you 2 tests; the purpose of which is solely to confirm that when service.doSomething is called, emailService.sendEmail is called with the person's email as a parameter.

@Mock 
private EmailService emailService;

@InjectMocks
private Service service;

@Captor
private ArgumentCaptor<String> stringCaptor;

@Test
public void test_that_when_doSomething_is_called_sendEmail_is_called_NO_MOCKING() {

    final String email = "[email protected]";

    // There is only one way of building an Address and it requires all these fields
    final Address crowsNest = new Address("334", "Main Street", "Gloucester", "MA", "01930", "USA");
    // There is only one way of building a Phone and it requires all these fields
    final Phone phone = new Phone("1", "978-281-2965");
    // There is only one way of building a Vessel and it requires all these fields
    final Vessel andreaGail = new Vessel("Andrea Gail", "Fishing", 92000);
    // There is only one way of building a Person and it requires all these fields
    final Person captain = new Person("Billy", "Tyne", email, crowsNest, phone, andreaGail);

    service.doSomething(captain); // <-- This requires only the person's email to be initialised, it doesn't care about anything else

    verify(emailService, times(1)).sendEmail(stringCaptor.capture());

    assertThat(stringCaptor.getValue(), eq(email));   
}

@Test
public void test_that_when_doSomething_is_called_sendEmail_is_called_WITH_MOCKING() {

    final String email = "[email protected]";

    final Person captain = mock(Person.class);
    when(captain.getEmail()).thenReturn(email);

    service.doSomething(captain); // <-- This requires the person's email to be initialised, it doesn't care about anything else

    verify(emailService, times(1)).sendEmail(stringCaptor.capture());   

    assertThat(stringCaptor.getValue(), eq(email));   
}

Why is it that my team is telling me not to mock the domain objects required to run my tests, but not part of the actual test? I am told mocks are for the dependencies of the tested service only. In my opinion, the resulting test code is leaner, cleaner and easier to understand. There is nothing to distract from the purpose of the test which is to verify the call to emailService.sendEmail occurs. This is something that I have heard and accepted as gospel for a long time, over many jobs. But I still can not agree with.

Nathan Hughes :

I think I understand your team's position.

They are probably saying that you should reserve mocks for things that have hard-to-instantiate dependencies. That includes repositories that make calls to a database, and other services that can potentially have their own rats-nest of dependencies. It doesn't include domain objects that can be instantiated (even if filling out all the constructor arguments is a pain).

If you mock the domain objects then the test doesn't give you any code coverage of them. I know I'd rather get these domain objects covered by tests of services, controllers, repositories, etc. as much as possible and minimize tests written just to exercise their getters and setters directly. That lets tests of domain objects focus on any actual business logic.

That does mean that if the domain object has an error then tests of multiple components can fail. I think that's ok. I would still have tests of the domain objects (because it's easier to test those in isolation than to make sure all paths are covered in a test of a service), but I don't want to depend entirely on the domain object tests to accurately reflect how those objects are used in the service, it seems like too much to ask.

You have a point that the mocks allow you to make the objects without filling in all their data (and I'm sure the real code can get a lot worse than what is posted). It's a trade-off, but having code coverage that includes the actual domain objects as well as the service under test seems like a bigger win to me.

It seems to me like your team has chosen to err on the side of pragmatism vs purity. If everybody else has arrived at this consensus you need to respect that. Some things are worth making waves over. This isn't one of them.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=139655&siteId=1