简体   繁体   中英

Can I mock a protected superclass method call in Java?

I have stumbled upon a testing problem of mocking a call of protected method from a super class.

eg :

public class A extends B {
    @Override
    protected int calculate(int x, int y) {
         int result = super.calculate(x, y);
         result += 10;
         return result;
    } 
}

So is it possible by any means to mock super.calcluate(x,y) ? I have tried it with spy but spy could not invoke super method cause it's out of scope I think.

I would imagine a mocking framework would allow for you to mock protected members, but if your mocking framework doesn't, have you considered creating a stub of the class?

public class AStub extends A {
    @Override
    protected int calculate(int x, int y) {
        // do stub calculation 
        return calculation;
    }
}

When you mock an object, you don't care about its implementation. You only add expectations about which method is called.

If you mock A, then your expectations will say something "I expect method calculate on A to be called with parameters 2 and 3, and the result will be 15".

You don't care what the calculate method does in practice, that's the whole point of mocking.

You shouldn't be mocking a call to super. A unit test is intended to test the functionality of a class, and the functionality of ancestor classes is a part of that functionality .

The only time you should need to mock a super class is when the ancestor class behaves badly - such as initializing outside classes as part of its own initialization (without the projection of dependency injection) or accessing external resources (without the projection of dependency injection). If you have access to the ancestor class, refactor it to remove these problems.

Unfortunately, I don't think there's an easy way to mock super.calculate .

I can see that you want to write a "clean" unit test for A that does not rely on B, which is a reasonable aim. But if A is calling a protected method on its superclass, Java makes it hard to decouple the two classes.

This is one of many examples where using inheritance couples classes more closely than you'd like.

Ideally A would wrap B rather than extending it (ie using composition rather than inheritance), and would then call myB.calculate rather than super.calculate .

Both classes could implement the same interface (eg Calculable) to allow them to be treated polymorphically by clients. This approach would requre B.calculate to be public, or at least package-public. Your unit test could set up an A that wraps a mock B, thereby solving your problem.

I think you have to choose between increasing the visibility of calculate(), as described above, and writing a clean unit test - I don't think you can have both.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM