6

I'll show my problem using this example:
I have a class with a method foo. That class has a subclass which overrides this method.
Subclass' method calls superclass' method. Can I verify that?
I don't want to test what foo in superclass does. I just need to verify that it was called. I know that refactoring could help (favour composition over inheritance, etc) but I am unable to do that.
Is there any way to achieve what I need?
Below is simple example of what I've tried

package tmp;
import org.junit.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.times;

public class Example {
    @Test
    public void test() {
        // given
        ChildClass childClass = new ChildClass();
        ChildClass spyChildClass = Mockito.spy(childClass);
        // when
        spyChildClass.foo(100);
        // then
        Mockito.verify((BaseClass) spyChildClass, times(1)).foo(101);
    }
}

abstract class BaseClass {
    public void foo(int n) {
        System.out.printf("BaseClass.foo(%d)%n", n);
    }
}

class ChildClass extends BaseClass {
    @Override
    public void foo(int n) {
        System.out.printf("ChildClass.foo(%d)%n", n);
        super.foo(n + 1);
    }
}

And this is the result:

ChildClass.foo(100)
BaseClass.foo(101)

Argument(s) are different! Wanted:
childClass.foo(101);
-> at tmp.Example.test(Example.java:19)
Actual invocation has different arguments:
childClass.foo(100);
-> at tmp.Example.test(Example.java:16)

Expected :childClass.foo(101);
Actual   :childClass.foo(100);
   <Click to see difference>

Obviously it's not what I wanted to see.

I can't modify BaseClass. I don't want to test BaseClass (I am not responsible for it). I don't even neet to know what exactly it does. I just need to verify that its method was called. Wnything else is not my problem. Its the problem of people who maintain BaseClass.

Rogério
  • 16,171
  • 2
  • 50
  • 63
Pawel P.
  • 3,731
  • 4
  • 20
  • 20
  • Does your BaseClass.foo() implementation *actually* have hard-to-test side-effects (as here) or does it have something that can easily be checked? (I'd leave mocking out of it in this case, if at all possible.) – Jon Skeet Mar 13 '14 at 14:54
  • Use the debugger -- put a breakpoint in your subclass `foo` method and step into the superclass method to see what is happening. – bedwyr Mar 13 '14 at 14:57
  • @JonSkeet yes. It does hard to test side effects. And I should not motify BaseClass at all... – Pawel P. Mar 13 '14 at 15:05
  • The short answer is no - in Mockito, there's no way to verify that the superclass method is called in this case. But it doesn't matter, because the right thing to do is to check that the behaviour of the class, not its implementation. I would post an answer to that effect, but @jmkgreen has already done so quite eloquently. – Dawood ibn Kareem Mar 14 '14 at 18:12

4 Answers4

8

Test the behaviour of the class, not it's implementation.

Write the test such that the method is called and is expected to do something. Next check the object now represents what you now expect it to represent.

If BaseClass.foo is expected to increment some counter by 100, yet Subclass.foo increments some counter by 50 then calls the superclass, verify that the counter is now 150 and not just 50.

Don't peek the how - they may change over time. Do test the behaviour. The method foo may do other things besides increase counters - check the state of the object not what it did.

jmkgreen
  • 1,633
  • 14
  • 22
  • I know all of that but... the case is much more complicated. As for now I need to see it the method of superclass was called. – Pawel P. Mar 13 '14 at 15:06
  • 3
    And the case is... The behaviour to be tested is calling the method of superclass. – Pawel P. Mar 14 '14 at 08:57
  • 1
    @PawelP. No. Calling a method isn't behaviour. It's implementation. Look at what the superclass method actually does, and verify that it happens. – Dawood ibn Kareem Mar 14 '14 at 18:09
  • @David Actually, this pattern/idiom where a derived class must call `super.baseMethod(...)` on the base class is a perfectly fine example where calling a method *is* behavior, *not* implementation. I can offer you a real-world example: the `Effect` base class of the "Animated Transitions" Swing library (described in the excellent book "Filthy Rich Clients" - see http://www.artima.com/forums/flat.jsp?forum=276&thread=217780). Every `Effect` subclass which overrides the `init(...)` method must call `super.init(...)` in order to obey the API contract. – Rogério Mar 18 '14 at 16:41
  • @Rogério The point is the client should not care that the object it is commanding is in fact a sub-class. The object, having been commanded, should now in be the state as predicted by the test covering it. – jmkgreen Mar 18 '14 at 16:45
  • @jmkgreen The situation I had in mind was when the test intends to explicitly cover the *sub*class, not the base class. Conversely, were I to write a test for the *base* class, I would not use any subclasses at all. – Rogério Mar 18 '14 at 19:17
  • @Rogério By _base class_ I assume you mean _superclass_. Assuming your _superclass_ is not marked _abstract_, certainly test it. Then make the same tests apply to the subclasses with the exception of the changed behaviours of course. At this level you would want to avoid duplicate test code of course but the subclass should still be fully exercised as in some future iteration that subclass may no longer extend a superclass. – jmkgreen Mar 19 '14 at 09:19
  • @jmkgreen Yes, I meant superclass. I agree with what you're saying. I just wanted to point out that the behavior of the superclass might not fully determine the behavior of subclasses. Then, you would need to write separate tests for the different behavior of each subclass, and one of the things to verify may be that a given subclass method override does call "super". I even have a real-world test suite which does just that [available online](http://code.google.com/p/jmockit/source/browse/trunk#trunk/samples/AnimatedTransitions/test/org/jdesktop/animation/transitions/effects). – Rogério Mar 19 '14 at 16:46
2

Possible solution: Make foo final, and decree that subclasses need to override some other method, the implementation of foo, instead of the actual foo.

abstract class BaseClass {

    private boolean myFooCalled;

    public final void foo(int n) {
        myFooCalled = false;
        fooImpl(int n);
        if (!myFooCalled) { ... }
     }

    public void fooImpl(int n) {
        myFooCalled = true;
        System.out.printf("BaseClass.foo(%d)%n", n);
    }

}

Notes: This is off the top of my head, so (1) I haven't tested it, (2) I'm not sure if this is what you really want, (3) I'm not sure whether your design ought to be improved. This is a general answer about "how you could make sure an overriding method calls the superclass method", not an answer tailored to your purposes.

ajb
  • 31,309
  • 3
  • 58
  • 84
1

I hesitate to give this answer because everyone here (including the OP) knows you can do this... but to answer the OP's question you can do this:

Instead of having

@Override
public void reset() throws IOException{
    // ...

    super.reset();
}

do this:

@Override
public void reset() throws IOException{
    // ...

    callSuperReset();
}

void callSuperReset() throws IOException {
    super.reset();
}

... and verify that callSuperReset was indeed called...

I am a mocking newb (no doubt it shows), and I thought for a long time the command was simply "Thou shalt not create methods just to suit your tests".

But in a previous question of mine, davidxxx in his answer says

In fact I would say rather : "thou shalt not create methods to suit your tests and that open the API of the application in an undesirable way"

Given that this method callSuperReset is package-private, is there any problem in principle with it? (other than its total inelegance)

Community
  • 1
  • 1
mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • This works as you can mock callSuper***() method of your service class to doNothing. Although the coverage goes up, the code looks really dirty. – coretechie Aug 11 '17 at 06:14
0

Yes, such a test can be written. And there is nothing wrong with wanting to do it, provided what you are testing is the contract of an API, not its implementation.

Here is a test for the contrived example in the question, using the JMockit mocking API:

@Test
public void subclassShouldObeyContractOfBaseClass(
    @Mocked final BaseClass anyBaseInstance)
{
    new ChildClass().foo(123);

    new Verifications() {{ anyBaseInstance.foo(anyInt); }};
}

If necessary (as dictated by the contract of BaseClass#foo(int)), the test could also verify the argument value passed to the base method as being n + 1 (or whatever).

Rogério
  • 16,171
  • 2
  • 50
  • 63
  • Rogério, can you please expand on this to tell us why this works? It seems to me that `base` and `new ChildClass()` are actually two different objects, and the fact that you can call a method on one object then verify it on another seems counter-intuitive. In what other circumstances, in JMockit, is it possible to call a method on one object and verify it on a different object? Also, for the benefit of other visitors to this page, you might like to tell us why you so frequently provide JMockit answers to questions tagged "mockito". – Dawood ibn Kareem Mar 18 '14 at 18:29
  • Ok. In the JMockit API, the application of `@Mocked` to a mock field or mock parameter means that we are *mocking a type*, rather than creating an isolated mock object. Since the type as a whole is mocked, *all* its instances become mocked instances for the duration of the test. So, the instance made available by JMockit as the value of the mock field/parameter is merely a representative of all such mocked instances. As such, any expectation recorded or verified on it applies equally to any and all matching invocations. – Rogério Mar 18 '14 at 19:03
  • @David As for your second question, I don't normally write answers for Mockito-specific questions, but I do sometimes enjoy answering questions which leave room for the use of other APIs. Like this one, unless you are telling me that once a question has a "mockito" tag it becomes off-limits for other mocking APIs. Is that the standard SO policy? " – Rogério Mar 18 '14 at 19:10
  • Rogério, (re first comment) that's quite interesting. I must learn more about this approach. I'm interested to think about whether the way this works would cause problems where you have several mocks of the same type, which is a situation I have in many of my tests. I might try reconstructing some of them using JMockit to see if I can get them to work. – Dawood ibn Kareem Mar 18 '14 at 19:39
  • Re the second comment, your answers have sometimes given me the impression that you have an agenda to push. I may be mistaken and I apologise if that is the case. I don't know if there is a "standard" SO policy about this, but I wonder how favourably the community would see someone who regularly provides C# answers to questioned tagged "Java". – Dawood ibn Kareem Mar 18 '14 at 19:39
  • Thanks. I understand your suspicion about some "evil agenda", because I often feel it myself when I see people saying or implying, for example, that making classes `final` in Java is a "bad practice". Because, really, it's not, and there is strong evidence in support of that. (I've written about this topic and others in the JMockit "About" page, if you're interested.) If I have an agenda, it's to defend sound OO design practices. Unfortunately, it so often seems that nobody else is interested in actually discussing the issues, and much less showing code or some other kind of evidence. – Rogério Mar 18 '14 at 21:51