validation在子类上调用此方法时是否调用了重写的超类方法

我将使用此示例显示我的问题:
我有一个方法foo的类。 该类有一个子类,它会覆盖此方法。
子类’方法调用超类’方法。 我可以validation吗?
我不想测试超类中的foo 。 我只需要validation它是否被调用。 我知道重构可能会有所帮助( 有利于组合而非inheritance等 ),但我无法做到这一点。
有没有办法实现我的需要?
以下是我尝试过的简单示例

 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); } } 

这就是结果:

 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);  

显然这不是我想看到的。

我无法修改BaseClass 。 我不想测试BaseClass (我不负责)。 我甚至不知道它究竟是做什么的。 我只需要validation它的方法是否被调用。 还有别的不是我的问题。 它是维护BaseClass的人的问题。

测试类的行为 ,而不是它的实现

编写测试,以便调用该方法并期望执行某些操作。 接下来检查对象现在代表您现在期望它代表的内容。

如果预期BaseClass.foo某个计数器递增100,但Subclass.foo会将某个计数器递增50,然后调用超类,validation计数器现在是150而不是50。

不要偷看它们 – 它们可能会随着时间而改变。 测试行为。 除了增加计数器之外,方法foo可以做其他事情 – 检查对象的状态而不是它做了什么。

可能的解决方案:make foo final,并判断子类需要覆盖其他一些方法,即foo的实现,而不是实际的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); } } 

注意:这不是我的头脑,所以(1)我没有测试过,(2)我不确定这是不是你真正想要的,(3)我不确定你的设计是否应该有待改进。 这是一个关于“如何确保覆盖方法调用超类方法”的一般答案,而不是根据您的目的定制的答案。

我不愿意给出这个答案,因为这里的每个人(包括OP)都知道你可以这样做……但是为了回答OP的问题,你可以这样做:

而不是拥有

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

做这个:

 @Override public void reset() throws IOException{ // ... callSuperReset(); } void callSuperReset() throws IOException { super.reset(); } 

…并verify callSuperReset确实被调用了……

我是一个嘲笑的新手(毫无疑问它显示),我想了很长一段时间命令只是“你不能创造方法只是为了适合你的测试”。

但在我之前的一个问题中,davidxxx在他的回答中说

事实上,我会说:“你不应该创建适合你的测试的方法,并以不合需要的方式打开应用程序的API”

鉴于这个方法callSuperReset是package-private,原则上它有什么问题吗? (除了它的总体不优雅)

是的,可以编写这样的测试。 如果您要测试的是API的合同而不是其实现,那么想要这样做并没有什么不妥。

这是使用JMockit模拟API测试问题中的人为示例:

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

如果需要(由BaseClass#foo(int)的合同规定),测试还可以validation传递给基本方法的参数值为n + 1 (或其他)。