检查方法未被调用

5

我希望检查某个方法是否未被运行,并尝试使用期望设置 times = 0;,但是我没有得到预期的行为。

例如,下面的测试通过了,虽然调用了 Session#stop 方法,但是期望中有一个 times = 0; 的条件:

public static class Session {
    public void stop() {}
}

public static class Whatever {
    Session s = new Session();
    public synchronized void method() {
        s.stop();
    }
}

@Test
public void testWhatever () throws Exception {
    new Expectations(Session.class) {
        @Mocked Session s;
        { s.stop(); times = 0; } //Session#stop must not be called
    };
    final Whatever w = new Whatever();
    w.method(); // this method calls Session#stop => the test should fail...
                // ... but it passes
}

注意:如果我用代码 { s.stop(); times = 1; } 替换代码,测试也会通过:我一定漏掉了什么明显的东西……
5个回答

9
意外的嘲弄行为是因为你在严格模拟的类型上无意中使用了部分模拟。在此情况下,使用 times = <n> 记录期望的结果,表示前n次匹配调用将被模拟,之后任何额外的调用都将执行原始的“未模拟”方法。如果使用常规模拟,您将获得预期的行为(即,在n次调用后会引发UnexpectedInvocation)。
编写测试的正确方式是:
public static class Session { public void stop() {} }
public static class Whatever {
    Session s = new Session();
    public synchronized void method() { s.stop(); }
}

@Test
public void testWhatever ()
{
    new Expectations() {
        @Mocked Session s;
        { s.stop(); times = 0; }
    };

    final Whatever w = new Whatever();
    w.method();
}

或者,也可以使用一个验证块来替代,通常在这种情况下更好:

@Test
public void testWhatever (@Mocked final Session s)
{
    final Whatever w = new Whatever();
    w.method();

    new Verifications() {{ s.stop(); times = 0; }};
}

你的回答中第一个代码块仍然无法正常工作。期望的次数为0或1都已通过!!使用StrictExpectations似乎可以正确解决这个问题。 - S.D.

3

关于这个问题,我在使用JMockit时遇到了麻烦,具体涉及times = 0和@Tested注释。

使用@Tested注释后,你仍然有一个“真实”的类,因此当在该真实类上注册Expectation或Verification(即使times = 0)时,JMockit会尝试执行该方法。解决方案是在Expectations中部分模拟该类:

@Tested
Session s;

new Expectations(Session.class) {{ 
   s.stop(); times = 0; } //Session#stop must not be called
};

这是我发现在@Tested类的方法中使用times=0的唯一方法。


0

可以尝试使用maxTimes,您还可以以静态方式引用stop():

@Test
public void test(@Mocked Session mockSession){

  final Whatever w = new Whatever();
  w.method();

  new Verifications(){
    {
       Session.stop();
       maxTimes = 0;
    }
  };
}

0

我在MockUp类中找到了一个解决方法 - 下面的测试按预期失败了 - 我仍然想知道为什么原始方法不起作用

@Test
public void testWhatever () throws Exception {
    new MockUp<Session>() {
        @Mock
        public void stop() {
            fail("stop should not have been called");
        }
    };
    final Whatever w = new Whatever();
    w.method();
}

-1

从记忆中,大概是这样的

verify( s  , times(0) ).stop();

这将起作用。问题在于,Whatever 中的 Session 不是您的 @Mock 对象,而是另一个对象,因此需要插入一个

w.s = s;

就在 w.method() 之前。

祝好,


我对jmockit不是特别熟悉,但是我没有看到任何 verify 方法,我相信模拟是仅基于类型的,因此 w.s = s 不是必要的。 - assylias
2
抱歉,那是Mockito的一个特性,我错过了jmockit标签 - 我犯了错误。不过,我认为我的论点是正确的,被mock的“Session”对象应该与“Whatever” session对象无关(至少在Mockito中是这样工作的),你怎么看? - Anders R. Bystrup
在jmockit中,您模拟的是类型而不是实例。因此,在我的示例中,所有Session实例都应该被模拟。除非我当然误解了什么;-) 不管怎样,谢谢。 - assylias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接