如何使用jmockit注入模拟依赖项

8

目前我试图了解@Injectable和@Tested注释是如何工作的。我已经进行了一些测试并理解了这个概念,但我不知道如何在现实世界应用中使用这些注释。

假设我们正在开发一个语言翻译器类,该类依赖于一个Web服务。 Web服务方法封装在单独的类中:

// class to test
public class Translator() {
    private TranslatorWebService webService;

    public String translateEnglishToGerman(String word){
        webService = new TranslatorWebService();
        return webService.performTranslation(word);
    }
}

// dependency
public class TranslatorWebService {
    public String performTranslation(String word){
    // perform API calls    
    return "German Translation";
    }
}

为了独立测试Translator类,我们希望模拟TranslatorWebService类。根据我的理解,测试类应该如下所示:

public class TranslatorTest {
    @Tested private Translator tested;
    @Injectable private TranslatorWebService transWebServiceDependency;

    @Test public void translateEnglishToGerman() {
        new Expectations() {{
            transWebServiceDependency.performTranslation("House");
            result = "Haus";
        }};

        System.out.println(tested.translateEnglishToGerman("House"));
    }
}

当我第一次执行这个测试用例时,我期望得到结果“Haus”。再看一眼,我发现这行代码


webService = new TranslatorWebService();

将始终使用真实实例覆盖注入的模拟实例。但是,我如何在不更改业务逻辑的情况下避免这种行为呢?

1个回答

9

很好的问题。需要注意的是,JMockit(或任何其他mocking API)对依赖注入的支持只用于当受测试代码实际依赖于其依赖项注入时使用。

示例Translator类并不依赖于TranslatorWebService依赖项进行注入; 相反,它通过内部实例化直接获得它。

所以,在这种情况下,您可以简单地模拟依赖项:

public class TranslatorTest {
    @Tested Translator tested;
    @Mocked TranslatorWebService transWebServiceDependency;

    @Test public void translateEnglishToGerman() {
        new Expectations() {{
            transWebServiceDependency.performTranslation("House");
            result = "Haus";
        }};

        String translated = tested.translateEnglishToGerman("House");

        assertEquals("Haus", translated);
    }
}

谢谢你的回答。我不知道@Mocked对象也会被注入。根据文档,只有@Injectable对象支持此功能:为了执行注入,测试类还必须包含一个或多个声明为@Injectable的模拟字段或模拟参数。仅使用@Mocked或@Capturing注释的模拟字段/参数不考虑进行注入。(官方文档:http://jmockit.github.io/tutorial/BehaviorBasedTesting.html#tested)但无论如何,这个解决方案对我很有效。非常感谢! - Philipp Waller
1
@Mocked对象不会被注入;它们的类会被模拟。 - Rogério
1
只是想指出,在这种情况下,通过设定期望的网络服务返回值为“Haus”,并断言测试主体返回值也为“Haus”,我们并没有实现一个有意义的测试。关于使用@Mocked的回答是正确的,但对我来说,更重要的是确保我们实际上实现了一些有用的测试,所以我建议重新构建测试主体 或者 修改测试内容,仅验证测试主体是否按预期参数调用了网络服务。 - unigeek
3
@unigeek 确实。这个测试只需要验证performTranslation("House")是否被调用,不需要更多;使用JMockit API,可以将期望的记录块替换为一个期望的验证块:new Verifications() {{ transWebServiceDependency.performTranslation("House"); }}; - Rogério

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