When to use mock objects in unit tests

jle :

I'm aware that there are many questions about mocking and testing, but I didn't find any that helped me perfectly, so I still have problems understanding the follwing:

Please correct me if I got this wrong, but as far as I see, unit tests are used to test the business logic of one particular class in isolation and if there are any objects needed from the outside they will be mocked. So for example if I have a management system for citizens of a simple city that adds citizens to a list and returns the citizen by their names (Assumtion: citizens consist of only a few basic personal information), like this:

public class ProcessClass {

    ArrayList<Citizen> citizenList = new ArrayList<Citizen>();

    public void addCitizen(Citizen citizen) {
        citizenList.add(citizen);
    }

    public Citizen getByName(String name) {
        for (Citizen c : citizenList) {
            if (c.getName().equals(name)) {
                return c;
            }
        }
        return null;
    }

}

If now I want to unit test my ProcessClass do I consider Citizen as an external feature that has to be mocked, or do I simply just create a Citizen for test purposes? If they are mocked, how would I test the method to get the object by its name, since the mock object is not containing the parameters?

Bentaye :

If they are mocked, how would I test the method to get the object by its name, since the mock object is not containing the parameters?

You can mock the call to getName, using mockito for example:

Citizen citizen = mock(Citizen.class);
when(citizen.getName()).thenReturn("Bob");

Here is an example of a test for your method

ProcessClass processClass = new ProcessClass();

Citizen citizen1 = mock(Citizen.class);
Citizen citizen2 = mock(Citizen.class);
Citizen citizen3 = mock(Citizen.class);

@Test
public void getByName_shouldReturnCorrectCitizen_whenPresentInList() {
    when(citizen1.getName()).thenReturn("Bob");
    when(citizen2.getName()).thenReturn("Alice");
    when(citizen3.getName()).thenReturn("John");

    processClass.addCitizen(citizen1);
    processClass.addCitizen(citizen2);
    processClass.addCitizen(citizen3);

    Assert.assertEquals(citizen2, processClass.getByName("Alice"));
}

@Test
public void getByName_shouldReturnNull_whenNotPresentInList() {
    when(citizen1.getName()).thenReturn("Bob");

    processClass.addCitizen(citizen1);

    Assert.assertNull(processClass.getByName("Ben"));
}

Note:

I would recommend mocking. Let's say you write 100 tests where you instantiate a Citizen class this way

Citizen c = new Citizen();

and a few months later, your constructor changes to take an argument, which is an object itself, class City for example. Now you have to go back and change all these tests and write:

City city = new City("Paris");
Citizen c = new Citizen(city);

If you mocked Citizen to start with, you wouldn't need to.

Now, as it is POJO and its constructor of the getName method might not change, not mocking should still be ok.

Guess you like

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