【问题标题】:How to capture DTO instantiated in service method using mockito如何使用 mockito 捕获在服务方法中实例化的 DTO
【发布时间】:2021-09-28 13:27:28
【问题描述】:

我有服务 A

@Service
class ServiceA {

    @Override
    public void foo(MyDto dto){};
}

和服务 B 一样

@Service
class ServiceB{

    @Override
    public void bar(){
        final MyDto myDto = new MyDto();
        serviceA.foo(myDto);
        if(myDto.check()){
            // block to test further
        }
    }
}

我尝试按照以下方式测试服务 B 栏方法

@Mock
private ServiceA serviceA
@InjectMocks
private ServiceB unit;

@Test
public void test(){
    ArgumentCaptor<MyDto> captor = ArgumentCaptor.forClass(MyDto.class);
    when(serviceA.foo(captor.capture()));
    MyDto dto = captor.getValue();
    dto.setCheck(true);
    unit.bar();
}

服务 b 中的“if”进入 else,因为它是错误的,我如何在服务 A 方法调用中传递存根 dto,我不能使用参数捕获器,因为在方法调用之前无法捕获值。 除了改变服务方式还有什么解决办法?

【问题讨论】:

    标签: java spring-boot junit mocking mockito


    【解决方案1】:

    您可以使用Mockito 中的when 来模拟外部调用行为。例如:

    MyDto mockedDto = new MyDto();
    mockedDto.setCheck(true);
    
    when(serviceA.foo(Mockito.any(MyDto.class))).thenReturn(mockedDto);
    
    unit.bar();
    
    assertEquals(expected, actual);
    
    

    您还可以验证它是否被调用(在调用unit.bar() 之后):

    verify(serviceA, times(1)).foo(any(MyDto.class));
    

    编辑:

    为了使上述代码能够正常工作,代码需要进行一些更改以使其更具可测试性。

    例如ServiceA 方法将返回MyDto:

    @Override
    public MyDto foo(MyDto dto){ /* do something */ };
    

    然后ServiceB 方法将是:

    @Override
    public void bar(){
        final MyDto myDto = new MyDto();
        MyDto updatedDto = serviceA.foo(myDto);
        if(updatedDto.check()){
            // do something
        }
    }
    

    【讨论】:

    • 当条件失败时:java: 'void' type not allowed here
    • 感谢@GauravkumarSingh 指出这一点。我编辑了我的答案。
    【解决方案2】:

    您可以使用 power Mockito 来实现此目的。将以下内容添加到您的测试类中。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ServiceB.class})
    

    并且测试实现如下。

    MyDto myDto = Mockito.spy(MyDto.class);
    myDto.setCheck(true);
    
    PowerMockito.whenNew(MyDto.class).withNoArguments().thenReturn(myDto);
    unit.bar();
    

    这里,我们创建了一个 myDto 的 spy 对象,并使用 powerMockito.whenNew 来传递这个对象。

    【讨论】:

      【解决方案3】:

      您可以尝试使用Mockito.doAnswer() 来设置模拟方法行为:

          @Test
          public void test() {
              doAnswer(invocation -> {
                  MyDto dto = invocation.getArgument(0);
                  dto.setCheck(true);
                  return null; // for void methods
              }).when(mockServiceA).foo(any());
              
              unit.bar();
          }
      

      【讨论】:

        【解决方案4】:

        我有兴趣尝试这里的一些建议,但我处理此类问题的一种方法是避免在我正在测试的方法中创建新的类实例。例如,将您的 MyDto 对象的创建提取到一个单独的方法中,为 MyDto 创建一个 mock,然后再使用一个 spy:

        服务 B:

        @Service
        class ServiceB{
        
            @Override
            public void bar(){
                final MyDto myDto = createDto();
                serviceA.foo(myDto);
                if(myDto.check()){
                    // block to test further
                }
            }
        
            // You don't need this annotation from Guava, but make the createDto method 
            // visible to your test
            //@VisibleForTesting
            MyDto createDto() {
               return new MyDto();
            }
        }
        

        然后在你的测试中:

        @Mock
        private ServiceA serviceA
        
        @Mock
        private MyDto mockDto;
        
        @Spy
        @InjectMocks
        private ServiceB underTest;
        
        // add a @Before and reset your mocks
        
        @Test
        public void test(){
        
            when(mockDto.getCheck()).thenReturn(true);
            when(underTest.createDto()).thenReturn(mockDto);
                  
            underTest.bar();
            // add asserts etc
        }
        

        【讨论】:

        • 废话 - 抱歉,我刚刚注意到您问题的一部分,您询问除了更改服务方法之外的任何解决方案。在这种情况下,如果可能的话,我会更改服务方法,以便您可以轻松地对其进行测试,而无需求助于 PowerMockito 之类的东西。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-10
        • 2021-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多