Bobzone
Bobzone

Reputation: 286

Unit testing with mock that has mock inside

I need to unit test a certain method that works on an object of type X. The X object has few normal fields like String, int etc. but it also has a List of types Y. This Y type is also quite complex. And to make my problem even more difficult, let's say that the type Y has another list of type Z.

Ideally, I need to mock object X, but tell it to inject another mock of type Y inside, which should also inject another mock Z to itself (to Y). How should I do that?

And if I have so many few-levels mocks how should I avoid writing 20 specifications of what these mocks should actually return upon calling their methods?

Or is this a type of problem where I should use Builders and actually build a real object?

Thanks alot, Bob.

Edit: just an example usage code on top from my head:

public String produceImportantStringOfImportantData(ObjectX x) throws ParseException {
    StringBuilder textResult = new StringBuilder();

    List<ObjectY> listOfY = x.getListOfY();

    if (listOfY.isValid()) {
        for (ObjectY y : listOfY) {
            for (ObjectZ z : y.getListOfZ()) {
                textResult.append("<font color='green'>").append(z.getField2).append("</font>").append(": ").append(z.getSomething())
                        .append(" Label : ").append(z.getParentComponent.getField()))
                        .append(" some important labels: ").append(z.getField()))
                        .append(" some important labels ").append(y.getAnotherField))
                        .append(" different, important label: ").append(y.getField()))
                        .append("<br/>");
            }
        }
    } 
    return textResult.toString();
}

Upvotes: 2

Views: 4997

Answers (2)

tddmonkey
tddmonkey

Reputation: 21184

Please don't use mocks for this as there's absolutely no need to, but as you already seem to be aware, will lead to painful and fragile tests.

Or is this a type of problem where I should use Builders and actually build a real object?

You don't even need a Builder (although I do recommend them) - just use a real object. Using a real object will lead to a test that's must more resilient to refactoring, will test what your code is actually doing and not just what you think it is.

Upvotes: 2

Boris van Katwijk
Boris van Katwijk

Reputation: 3188

Assuming you are using some mocking framework like Mockito, you could simply do:

X mockX = Mockito.mock(X.class);
Y mockX = Mockito.mock(Y.class);
Z mockX = Mockito.mock(Z.class);
Mockito.when(mockY.getZ()).thenReturn(mockZ);
Mockito.when(mockX.getY()).thenReturn(mockY);

If this gets too deep (you mention 20 specifications), it could be a sign that you are violating the Law of Demeter or the Single Responsibility Principle. In this case it means that your classes execute too much logic themselves rather than accepting some processors in their constructor which do the actual work.

If you would do this, you can test the processors separately and easily, and testing the entire process.

Upvotes: 8

Related Questions