1

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?

migrated from codereview.stackexchange.com Dec 31 '15 at 16:46

This question came from our site for peer programmer code reviews.

  • You appear to be asking a pointed question about a concept, rather than asking for a general critique. Migrating to Programmers. – 200_success Dec 31 '15 at 16:46
4

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.

1

The code under test is not really making anything easier for you. If you could reorganize that code you would find it much easier to test. Otherwise you are stuck with these overly complicated tests that jump through all the hoops to get from ICar to MarkId.

It is hard to make any reorganization recommendations with only one function given. It looks like your function is asking for a car when it really needs a List to get you your List. This forces your function to look through different Objects to get what it needs. The easier more testable approach is to ask for the parameters you need upfront and work from there. This will cut all the lookup code out and make your tests much easier to write.

Breaking the code down into smaller pieces will make your code rely on multiple tests to prove that it is working instead of one overly complicated hard to maintain master test which does everything.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.