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?
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.