代码之家  ›  专栏  ›  技术社区  ›  Brother Eye

JUnit-如何模拟局部变量对象方法调用

  •  0
  • Brother Eye  · 技术社区  · 2 年前

    我的用例代码如下:

    我的班级:

    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class MyClass {
        public String getString() {
            // object need mocking
            ObjectMapper mapper = new ObjectMapper()
            try {
                              // method need mocking
                return mapper.writeValueAsString(List.of("1", "2"));
            }
            catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }
    

    测试代码:

    @Test
    public void test_get_string() throws Exception {
        ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
        Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
              .thenThrow(JsonProcessingException.class);
        
        Assertions.assertThrows(RuntimeException.class, () -> {
            new MyClass().getString();
        });
        // -> failed
        // Expected RuntimeException but nothing was thrown
    }
    

    我想嘲笑 ObjectMapper 并使其投掷 JsonProcessingException 在…上 writeValueAsString 方法调用,但测试一直失败,就好像它根本没有使用我的模拟映射器一样。但是如果我将映射器设为的属性 MyClass 并嘲笑它,然后测试通过。

    public class MyClass {
        // make mapper a property
        private ObjectMapper mapper;
        
        public MyClass(ObjectMapper mapper) {
            this.mapper = mapper;
        }
    
        public String getString() {
            try {
                return mapper.writeValueAsString(List.of("1", "2"));
            }
            catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    @Test
    public void test_get_string() throws Exception {
        ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
        Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
              .thenThrow(JsonProcessingException.class);
        
        Assertions.assertThrows(RuntimeException.class, () -> {
            // pass mapper on creating object
            new MyClass(mapper).getString();
        }); // -> success
    }
    

    如果我不想让mapper成为我类的一个属性,我应该如何正确地模拟它?

    1 回复  |  直到 2 年前
        1
  •  4
  •   VHS    2 年前

    不能使用Mockito模拟方法中声明的局部变量。你可以 ObjectMapper 你班的财产 MyClass 。但如果您不想这样做,另一种选择是在 类名 返回的实例 ObjectMapper

    public class MyClass {
        public String getString() {
            // object need mocking
            ObjectMapper mapper = createObjectMapper()
            try {
                              // method need mocking
                return mapper.writeValueAsString(List.of("1", "2"));
            }
            catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
       protected ObjectMapper createObjectMapper() {
           return new ObjectMapper();
       }
    }
    

    然后在您的测试用例中,您可以 spy 您的类并存根这个新方法,以返回一个mocked ObjectMapper 如下所示:

    @Test
    public void test_get_string() throws Exception {
        ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
        Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
              .thenThrow(JsonProcessingException.class);
        MyClass myClass = Mockito.spy(new MyClass ());
        doReturn(mapper).when(myClass).createObjectMapper();
        Assertions.assertThrows(RuntimeException.class, () -> {
            myClass.getString();
        });
    }