在Mockito中使用泛型处理匿名类

e4yzc0pl  于 2022-11-08  发布在  其他
关注(0)|答案(1)|浏览(245)

我正在尝试使用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}
);

看起来stub不工作,因为我总是得到“null”为此行-

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

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

5sxhfpxr

5sxhfpxr1#

是的,这是因为匿名内部类的缘故。具体来说,您告诉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,就像它在GuiceGuava中的表亲一样;与Guice和Guava不同,您不能在测试中创建自己真实TypeReference并与Mockito的eq进行匹配。
您仍然不应该模拟TypeReference,但是您还需要调整对它进行Assert的方式:

  • 如果Jackson允许的话,将匿名TypeReference子类提取到一个命名的等价类中,然后使用isA检查其类型。
  • 将TypeReference提取为可见常量并检查其引用是否相等。
  • 使用Captor,并使用getType检查TypeReference的泛型型别。
  • 创建一个使用getTypeArgumentMatcher实现,并使用argThat使用它。
  • 切换到ArgumentMatchers.any()ArgumentMatchers.<TypeReference<Map<String, Object>>>any(),这是以前在MatchersMockito接口上的值。无论如何,这个值不太可能改变,所以从实用的Angular 来说,忽略检查比说服PowerMock更容易阅读和测试。
  • 理想情况下,尽可能使用真正的依赖关系,并且只检查函数是否工作,而不是检查是否与正确的协作者进行了交互。无论如何,工作函数是您所追求的,对吗?

相关问题