JMockit - Expectations - 匹配包含模拟对象作为参数的方法调用

3

我正在使用JMockit来测试一个应用程序(Java 8、Junit 4.12、JMockit 1.17)。

我有一些代码,它应该将一些数据上传到一些端点列表中。

实际上执行上传到终端点的类名为PlatformDataUploader。这个类有一个方法,叫做“upload”,用于执行单个端点的“上传”。此方法接受目标名称(一个由两个字符组成的字符串)和要上传的对象作为参数。根据目的地的名称,它构建应该发送数据的URL。

我想创建一个测试,仅确认执行应用程序时,PlatformDataUploader的上传方法被调用了预期的次数(7次,因为应用程序当前配置为将数据上传到7个端点)。我希望确认传递给上传方法的目标字符串是预期的,但我不关心所发送的数据(由PlatformInstallationData的实例表示)。

简化版本的应用程序代码如下:

 ...

 private boolean uploadToServices(final List<String> serviceNames) {

    boolean allGood = true;

    PlatformDataUploader platformDataUploader = new PlatformDataUploader();

    for (String serviceName : serviceNames) {

        LOG.info("Attempting to upload to " + serviceName + "...");

        // construct object to send
        PlatformInstallationData platformInstallationData = new PlatformInstallationData();

        ...
        // code here that adds content to platformInstallationData
        ...

        // send object to endpoint of this service
        allGood = allGood &&
            platformDataUploader.upload(serviceName, platformInstallationData);
    }

    return allGood;
}

以下是测试代码的简化版本:
@Test
public void whenUploadThenExpectedCallsToUploader(@Mocked final PlatformDataUploader platformDataUploader,
        @Mocked final PlatformInstallationData platformInstallationData)
                throws IOException {

    UploaderApplication target = new UploaderApplication();

    new Expectations() {
        {
            platformDataUploader.upload("AP", platformInstallationData);
            result = true;
            times = 1;

            platformDataUploader.upload("VV", platformInstallationData);
            result = true;
            times = 1;
            ...
            THE REST OF THE EXPECTED CALLS 
            ...


        }
    };

    target.execute(params);
}

当我执行测试时,会出现“mockit.internal.MissingInvocation: Missing 1 invocation”错误。它指的是我定义的期望中的第一行。
问题似乎在于upload方法的第二个参数,即模拟的PlatformInstallationData与应用程序代码中创建的实例不匹配(尽管该实例也应该被模拟)。
为了试图理解发生了什么,我进行了一个测试,其中我从upload方法中删除了第二个参数(从应用程序代码和测试代码中),因此它只需要将一个字符串(例如“AP”)与该点中存在的字符串进行匹配,在这种情况下,它确实匹配正确。所以我知道,使用这种方法,它可以正确匹配字符串,但无法匹配模拟的PlatformInstallationData对象。
我对这种行为感到惊讶,因为我确信之前我能够创建包括模拟对象作为方法参数的期望,并且我认为它们会正确匹配。
请问是否有人能够解释这种行为,并可能建议如何进行测试。非常感谢!
1个回答

1
我找到了一种编写测试的方法,使其匹配调用,并确认已向上传递方法传递了预期的调用次数和预期的字符串作为第一个参数。我对第二个参数使用了“any”占位符。这有点丑陋,因为我必须进行转换。如下所示:
@Test
public void whenUploadThenExpectedCallsToUploader(@Mocked final PlatformDataUploader platformDataUploader,
        @Mocked final PlatformInstallationData platformInstallationData)
                throws IOException {

    InstallPlatformCommand target = new InstallPlatformCommand(Action.INSTALL_PLATFORM);

    new Expectations() {
        {
            platformDataUploader.upload("AP", (PlatformInstallationData) any);
            result = true;
            times = 1;

            platformDataUploader.upload("VV", (PlatformInstallationData) any);
            result = true;
            times = 1;

            ...
            The rest of the expected calls
            ...
        }
    };

    target.execute(params);
}

我相信还有其他方法可以做到这一点。如果您有更简便的方法,请告诉我。


2
你可能不需要模拟 PlatformInstallationData,所以我建议移除那个模拟参数。一个替代 (X) any 的方法是使用 withInstanceOf(PlatformInstallationData.class)。最后,你可以通过使用 withCapture(List<String>) 将传递给 "serviceName" 参数的值序列捕获到本地列表中,并稍后使用 assertEquals 将其与输入列表进行比较;这将允许测试记录对 upload 方法的单个期望。 - Rogério

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