Java – Testing using mocking, must I mock all dependencies too

javamockingspringunit testing

I have the following method to test:

public List<MarkId> getMarkIdList(ICar carDoc) {

    ICourseCar courseCarDoc = courseCarRep.get(carDoc);

    List<MarkWag> markWagList = courseCarDoc.getMarks();

    List<Integer> markIdList = new ArrayList<>();

    for (MarkWag markWag : markWagList)
        markIdList.add(markWag.getMarkId());

    List<ICourseMark> courseMarkDocList = courseMarkRep.getList(markIdList);

    List<MarkId> markIds = new ArrayList<>();

    for (ICourseMark courseMark : courseMarkDocList)
        markIds.add( new MarkId(courseMark.getMarkDescriptor()));

    return markIds;
}

This is the unit test I created:

@RunWith(PowerMockRunner.class)
@SpringApplicationCourseuration(classes=SpringConf.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.management.*"})
public class CourseCarTest {

    @Mock private CourseCarRepImpl courseCarRep;
    @Mock private CourseMarkRepImpl courseMarkRep;
    @Mock private CourseCarDoc courseCat;
    @Mock private ICourseCar courseCarDoc;
    @Mock MarkWag markWag;
    List<MarkWag> markWagList = new ArrayList<>();
    @Mock  ICourseMark courseMark;
    List<ICourseMark> courseMarkDocList = new ArrayList<>();
    @Mock ICar carDoc;

    @InjectMocks
    @Autowired
    @Qualifier("CourseCarBO")
    private CourseCarBO courseCarBo;

    @Before
    public void setup() throws Exception {

        markWagList.add(markWag);
        courseMarkDocList.add(courseMark);

        whenNew(CourseCarRepImpl.class).withAnyArguments().thenReturn(courseCarRep);
        whenNew(CourseMarkRepImpl.class).withAnyArguments().thenReturn(courseMarkRep);
        when(courseCarRep.get(anyInt())).thenReturn(courseCarDoc);
        when(courseCarDoc.getMarks()).thenReturn(markWagList);
        when(markWag.getMarkId()).thenReturn(1);
        when(courseMarkRep.getList(anyList())).thenReturn(courseMarkDocList);
        when(courseMark.getMarkDescriptor()).thenReturn(1);
    }

    @Test
    public void testGetMarkIdList() {

        courseCarBo.getMarkIdList(1);

        verify(courseCarRep).get(anyInt());
    }
}   

My intention was to have an hybrid approach, both mocking some objects and injecting others with Spring. However I noticed that as I mock one of the objects, I have to mock the others that follow as they depend on each other. So it looks to me like it's not really possible to have an hybrid approach.

Can you spot anything wrong in the approach used in this unit test?

When I have to mock everything, it looks to me like if I'm not testing anything.

Is there a better way? Or my approach is correct, and mocking everything is the way to go?

Best Answer

You should begin by moving from procedural programming, which your code currently is, to OOP.

"I have objects," you may protest, but in fact your objects do not encapsulate anything. In a true OO world, your code should look like this:

public List<MarkId> getMarkIdList(ICar carDoc)
{
    return courseCarRep.get(carDoc).getCourseMarkIdsAsList();
}

The get() method of courseCarRep returns a CourseCar object, which already has everything it needs within it and the getCourseMarkIdsAsList() is a public method the CourseCar class provides to hide the more complicated logic behind it (which is taking its list of CourseMarks and returning only their identifications).

Which may bring up a question: Why do I need the getMarkIdList(ICar) method in the first place?


So how do I unit test the current getMarkIdList method?

Simple answer: you do not.

More elaborate answer: The procedure you are trying to test seems to be so high in your application layer it is no longer considered a single unit and therefore should not be tested using unit tests.

If you want to test that the method works, use integration testing. If you want to focus on unit testing, test the following objects and highlighted methods instead:

  • courseCarRep::get
  • courseCarDoc::getMarks
  • courseMarkRep::getList
  • courseMark::getMarkDescriptor

If you test, that those methods work under all conditions, you can be pretty sure, your procedure will work as well.

Trust. Your. Tests.

What should I (generally) really mock, when doing unit tests?

You should only mock the behaviour of objects which are necessary for the test to pass. Everything else should be replaced by a dummy or null (if possible).

Null values are a great indicator that something is unused. If I go through a unit test and see someone pass a null instead of an object, I safely assume that specific variable has nothing to do with the test itself, it is not part of it, and even if I replace the null object with an actual instance, the test results cannot change.


Also try to remove the new operator from your business logic. Either replace it by factories, if you feel like you really need to create something, or use dependency injection.