【问题标题】:spring mockito test failed when call a mock method调用模拟方法时弹簧模拟测试失败
【发布时间】:2019-03-21 04:04:50
【问题描述】:

这里有两个 Spring Service;

public interface Service01 {
     String test01(String name);
}

@Service 
public class Service01Impl implements Service01 { 

     @Override
     public String test01(String name) {
          if (!"123".equals(name)) {
              throw new RuntimeException("name is illegal");
          }
          return name;
     }
}

@Service 
public class Service02 { 
     @Autowired
     private Service01 service01;
     @Autowired
     private Service03 service03;

     public String test02(String name) {
          service03.checkNameNotNull(name);
          return service01.test01(name);
     }
}

@Service 
public class Service03 { 
     public void checkNameNotNull(String name) {
          if (name == null) {
              throw new RuntimeException("name is null");
          }
     }
}

那么这是我的测试类:

public class TestCalss {
    @Mock
    private Service01 service01;
    @Autowired
    private Service02 service02;

    @Test
    public void testMock() {
        // mock service
        when(service01.name("abc")).thenReturn("mock return abc");

        // here will be success
        String nameAbc = service01.test01("abc")
        Assert.assertEquals("mock return abc", nameAbc);

        // but here got exception: `name is illegal`
        String name = service02.test02("abc");
        Assert.assertEquals("mock return abc", name);
    }

}

第一季度: 为什么在行出现异常:String name = service02.test02("abc");

java.lang.RuntimeException: name is illegal

Q2:当我调用Service02 时如何模拟Service01

--编辑

Q2 用@InjectMocks 修复

第三季度: 如何只模拟Service01,但不模拟Service03

我想让这张支票没有模拟 service03.checkNameNotNull(name);

【问题讨论】:

    标签: spring spring-boot mockito junit4


    【解决方案1】:

    您需要在设置模拟时使用 ArgumentMatcher - 而不是传入字符串文字 "abc",您需要让您的行看起来像 when(service01.name(eq("abc"))).thenReturn...

    如果您不介意传入的实际字符串是什么,那么您还可以使用其他匹配器,例如 anyString()

    您也不想在测试中使用@Autowired - 如果您希望被测类自动将模拟注入其中,那么您需要实例化模拟并注入它们而不是真正的 Spring bean。

    最直接的方法是使用MockitoJUnitRunner@InjectMocks 注释:

    @RunWith(MockitoJUnitRunner.class)
    public class TestCalss {
        @Mock
        private Service01 service01;
        @InjectMocks
        private Service02 service02;
    
    ...
    

    要注入一个非模拟类,您需要使用 @Before 注释,并通过某种方式将对象传递到类中(就像在纯 Java 代码中一样)。

    我的首选方法是使用一个构造函数将依赖项设置为类上的私有 final 字段,但 Spring 还提供了一个类 ReflectionTestUtils,如果你真的想坚持使用私有字段,则可以使用它。二传手。

    所以是这样的:

    @Service 
    public class Service02 { 
    
         private final Service01 service01;
         private final Service03 service03;
    
         @Autowired
         public Service02 (Service01 service01, Service03 service03) {
             this.service01 = service01;
             this.service03 = service03;
         }
    
        ...
    }
    
    @RunWith(MockitoJUnitRunner.class)
    public class TestClass {
    
        @Mock
        private Service01 service01;
    
        private Service02 underTest;
    
        @Before
        public void setup() {
            this.underTest = new Service02(this.service01, new Service03());
        }
        ...
    }
    

    如果你想在一个你想模拟的对象上调用一个真正的方法,那么这可能表明你的类做的太多了,实际上应该是两个或更多更小的类,但这也是可能的:@ 987654331@

    【讨论】:

    • when(service03.checkNameNotNull()).thenCallRealMethod();出现错误:when(T) cannot applied to void,?
    • doCallRealMethod().when(service03).checkNameNotNull(any()) 这行得通。谢谢@玩家一号
    猜你喜欢
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 2023-02-06
    • 2021-10-16
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多