【问题标题】:Jmockit: how to verify a method was called on a specific instanceJmockit:如何验证在特定实例上调用了方法
【发布时间】:2015-04-17 08:27:50
【问题描述】:

我在 Java 1.7 上使用 JMockit 1.15。

我想验证一些经过测试的代码是否首先创建了特定类 (ExampleClass) 的新实例,然后在该实例上调用方法。 我尝试了很多方法,以多种不同的方式使用验证、期望、@Mocked、@Injectable,但我无法成功。

下面是一个例子。

ExampleClass 是一个类;我要模拟它,我想验证它是如何使用的。

public class ExampleClass {

    final double id = Math.random();

    public ExampleClass() {
    }

    public void doSomething() {
        System.out.println("I did something - " + id);
    }

}

ExampleClassUser 包含我要测试的逻辑。

注意 wrongUsage() 构建了两个 ExampleClass 实例,但总是在第一个实例上调用 doSomething()。

public class ExampleClassUser {

    public void rightUsage() {
        final ExampleClass exampleClass1 = new ExampleClass();
        exampleClass1.doSomething();

        final ExampleClass exampleClass2 = new ExampleClass();
        exampleClass2.doSomething();
    }

    public void wrongUsage() {
        final ExampleClass exampleClass1 = new ExampleClass();
        exampleClass1.doSomething();

        final ExampleClass exampleClass2 = new ExampleClass();
        exampleClass1.doSomething();
    }

}

ExampleClassUserTest 是我想要构建的测试。 它应该检查每个方法调用 rightUsage() 或 wrongUsage()

  1. 创建一个新的 ExampleClass 实例
  2. 在该实例上调用 doSomething
  3. 创建另一个 ExampleClass 实例
  4. 在第二个实例上调用 doSomething

应用到 rightUsage() 测试应该成功,应用到 wrongUsage() 测试应该失败。

当然,我的测试无法做到这一点,因为它只是检查是否正在创建新实例以及是否执行了两个方法调用,但**不检查在哪个实例上执行了方法调用” .

import mockit.FullVerificationsInOrder;
import mockit.Mocked;

import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith(mockit.integration.junit4.JMockit.class)
public class ExampleClassUserTest {

    @Test
    public void testUsage(final @Mocked ExampleClass exampleClass) {

        //new ExampleClassUser().rightUsage();
        new ExampleClassUser().wrongUsage();

        new FullVerificationsInOrder() {{
            new ExampleClass();
            exampleClass.doSomething();

            new ExampleClass();
            exampleClass.doSomething();
        }};

    }

}

【问题讨论】:

  • 测试可以用JMockit写,但是看起来不太好。我认为你应该描述你需要解决的实际测试问题,而不是展示一个人为的例子。我敢打赌,真正的问题会更容易。
  • 好吧,我试图通过一个特别的例子使它更清晰和可执行,但是,正如你问我的那样,我会报告真实的情况。
  • 我有一个代表 ssh 调用的类(使用构造函数 SshCommand(String command, Host targetHost))并有一个 execute() 方法。在整个测试代码中使用了这个类的许多实例。我希望我的单元测试检查创建了哪些实例以及在哪些实例上使用了 execute() 方法。
  • 我在下面看到了您的示例解决方案,这正是我的想法(我只需要等待新版本发布)。你为什么说“它看起来不好看”?
  • 在 JMockit 1.16 或更早版本上看起来不太好,因为测试必须使用带有 Invocation 参数的 Delegate 对象,以便它可以访问新实例和将其保存到测试类中的一个字段中。 JMockit 1.17+ 在将方法期望与先前匹配的构造函数期望匹配时使用内部“等效实例”映射,因此测试变得更加简单。

标签: java unit-testing jmockit


【解决方案1】:

使用 JMockit 1.17 或更高版本,以下版本的测试将起作用(即,它会按预期失败):

@Test
public void testUsage(@Mocked ExampleClass exampleClass)
{
    new ExampleClassUser().wrongUsage();

    new FullVerificationsInOrder() {{
        ExampleClass e1 = new ExampleClass(); // matches first new instance
        e1.doSomething(); // matches only on instances equivalent to "e1"

        // Same as before, now for the second instance.
        ExampleClass e2 = new ExampleClass();
        e2.doSomething(); // fails with a "missing invocation"
    }};
}

【讨论】:

  • 你认为在验证块中写inline,创建对象和方法调用(new ExampleClass().doSomething();)而不是把新创建的实例赋值给一个变量是一样的吗? (它似乎有效,但我不太确定它是否正确)。
  • 是的,是一样的;为了清楚起见,我只使用了局部变量。
【解决方案2】:

测试实例化对象的方法有点困难,因为在方法之外您无权访问新创建的实例。对此的解决方案是使用创建ExampleClass 实例的Factory,您将其作为依赖项传递给ExampleClassUser 的工厂。现在您可以控制,因为您可以模拟工厂以返回您需要的任何对象(模拟、自定义单元测试对象等),并且由于您可以访问这些对象,因此您可以对它们进行断言。

如果您不需要确保调用了对象/方法,另一种方法是不断言调用了正确的对象/方法,而是断言这些调用的效果。这将有利于将测试与方法实现分离。仅当doSomething() 具有无需访问对象即可检测到的副作用并且可能涉及复杂设置时,此方法才可行。

例如,如果doSomething() 返回一个素数,并且每次调用它都会返回下一个素数(第一次调用将返回 2,第二次调用将返回 3,依此类推),并且测试的方法将两个数字相加并返回它们(是的,我知道愚蠢的例子),您可以检测是否在两个不同的对象上调用了 doSomething(),因为它们的总和为 4。如果在同一个对象上调用 doSomething(),则返回结果为 5。

【讨论】:

  • 您认为这是 Jmockit 限制还是不好的测试模式?
  • 这不是 jmockit 的限制,而是不可测试的代码。基本上,由于对创建对象的引用只能在封闭方法的范围内访问,因此从方法外部几乎没有什么可以做的。现在,如果您想测试是否在适当的对象上调用了 doSomething(),那么您可以将对象实例化移到方法之外,您(和 jmockit)可以访问的地方,或者......我将这种方法添加到答案中这对浏览此页面的其他人会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 1970-01-01
相关资源
最近更新 更多