【问题标题】:Handle anonymous class with Generics in Mockito在 Mockito 中使用泛型处理匿名类
【发布时间】:2016-11-29 12:51:03
【问题描述】:

我正在尝试使用 Powermockito 为以下方法编写单元测试 -

public String getGenerator(String json) throws IOException {
    String jwt = "";
    ObjectMapper mapper = new ObjectMapper();

    // convert JSON string to Map
    Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
    }); // Here passing TypeReference annonymously

    // Create a JWT
    JWTGenerator generator = new JWTGenerator();
    if (map != null && map.size() > 0) {
        jwt = generator.createJWT(map);
    }

    return jwt;
}

我把测试方法写成-

@Test
public void testGetJWTGenerator() throws Exception {
    ObjectMapper mockMapper = PowerMockito.mock(ObjectMapper.class);
    PowerMockito.whenNew(ObjectMapper.class).withNoArguments().thenReturn(mockMapper);

    JWTGenerator mockJWTDecoder = PowerMockito.mock(JWTGenerator.class);
    PowerMockito.whenNew(JWTGenerator.class).withNoArguments().thenReturn(mockJWTDecoder);

    Map<String, Object> anyMap = new HashMap<String, Object>();
    anyMap.put("testStr", new Object());

    TypeReference<Map<String, Object>> mockTypeReference = (TypeReference<Map<String, Object>>) PowerMockito.mock(TypeReference.class);
    PowerMockito.whenNew(TypeReference.class).withNoArguments().thenReturn(mockTypeReference);

    PowerMockito.when(mockMapper.readValue(Mockito.anyString(), Mockito.eq(mockTypeReference))).thenReturn(anyMap);
    PowerMockito.when(mockJWTDecoder.createJWT(anyMap)).thenReturn(Mockito.anyString());
    utilityController = new UtilityController();
    utilityController.getJWTGenerator("{\"someStr\":\"someStr\"}");
    Mockito.verify(mockJWTDecoder, Mockito.times(1)).createJWT(anyMap);
}

当我运行这个测试时,我总是说失败 -

Wanted but not invoked:
jWTGenerator.createJWT(
    {testStr=java.lang.Object@24bdb479}
);

看起来存根不起作用,因为我总是为这一行得到“null” -

Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
        }); // Here passing TypeReference annonymously

是因为 TypeReference 类的匿名实例化吗?

【问题讨论】:

    标签: java unit-testing mockito powermockito


    【解决方案1】:

    是的,这是因为匿名内部类。具体来说,您告诉 PowerMockito 替换对 new TypeReference 的调用:

    PowerMockito.whenNew(TypeReference.class).withNoArguments()
        .thenReturn(mockTypeReference);
    

    但您实际上创建的是一个扩展 TypeReference 的匿名类,而不是 TypeReference 本身:

    Map<String, Object> map = mapper.readValue(
        json, new TypeReference<Map<String, Object>>() {});
    

    这对您来说尤其棘手。我在这里的一般建议是“don't mock data objects”,就像 TypeReference,因为它是一个不依赖于标记/值/数据的对象,它在反射上很重要,但它也是 doesn't support equals,就像它在 Guice 和 Guava 中的表亲一样;您不能只在测试中创建自己的真实 TypeReference 并将 eq 与您班级中的真实 TypeReference 进行比较。

    您仍然不应该模拟 TypeReference,但您还需要调整针对它的断言方式:

    • 如果 Jackson 允许,将匿名 TypeReference 子类提取到一个已命名的等效子类,然后使用 isA 检查其类型。
    • 将 TypeReference 提取为可见常量,并检查其引用相等性。
    • 使用 Captor 并稍后使用反射检查 TypeReference 的泛型类型。
    • 切换到Mockito.&lt;TypeReference&lt;Map&lt;String, Object&gt;&gt;&gt;any(),这在技术上属于Matchers 类,或者在较新版本的Mockito 中使用ArgumentMatchers。无论如何,该值都不太可能发生变化,因此从务实的角度来看,您的系统和测试可能会比不考虑检查而说服 PowerMock 更具可读性和健壮性。
    • 理想情况下,尽可能使用真正的依赖关系,并检查函数是否正常工作,而不是检查您是否以您的实施方式与正确的协作者进行交互。无论如何,工作功能是您所追求的,对吧?

    【讨论】:

    • 感谢您的详细解释。我切换到 Mockito.>> any() 并且它有效。
    • @Jeff bowman,我也面临同样的问题。你能解释更多或提供一个例子吗?
    • @Nimesh 我可以回答具体问题,但在不知道重点领域或您的限制的情况下,我不知道该写什么我还没有写过。至于例子,我列出了足够多的技术和足够多的可能适用的方法,我不确定是否有必要先为每个技术编写一个具体的例子。
    • 你能检查我在这里发布的问题吗:stackoverflow.com/q/49672891/4418951
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 2011-02-18
    • 2012-08-09
    相关资源
    最近更新 更多