简体   繁体   English

单元测试:无法使用 ArgumentCaptor

[英]Unit Test: Cannot use ArgumentCaptor

I am trying to create a unit test for the following method that calls another private method:我正在尝试为调用另一个私有方法的以下方法创建一个单元测试:

public CommandDTO create(final LabelRequest request) {
        return saveLabel(new Label(), request);
}

private CommandDTO saveLabel(final Label label, final LabelRequest request) {
        label.setName(request.getName());
        final Label saved = labelRepository.saveAndFlush(label);

        return CommandDTO.builder().uuid(saved.getUuid()).build();
}

It returns "nullpointer exception for the saved parameter as I did not mock in in my test.它返回“已saved参数的空指针异常,因为我没有在测试中模拟。


Here is the Unit Test.这是单元测试。 I added 2 question as comment (Q1 and Q2).我添加了 2 个问题作为评论(Q1 和 Q2)。 Could you please clarify me what is wrong?你能澄清一下有什么问题吗?

@RunWith(MockitoJUnitRunner.class)
public class LabelServiceImplTest {

    @Mock
    private LabelRepository labelRepository;

    @InjectMocks
    private LabelServiceImpl labelService;
        
    @Captor
    ArgumentCaptor<Label> labelCaptor;

    @Test
    public void test_create {
            
        // Q1: I am not sure if the following parts are needed
        final LabelRequest request = new LabelRequest();
        request.setName("Label Name");
        final Label label = new Label();
        label.setName(request.getName());


        // Q2: I think there is no need to mock saveAndFlush method. 
        // But in this scene it returns "nullpointer exception"
        when(labelRepository.saveAndFlush(any())).thenReturn(label);


        CommandDTO result = labelService.create(request);

        Mockito.verify(labelRepository).saveAndFlush(labelCaptor.capture());
        final Label value = labelCaptor.getValue();
                
        // then make necessary assertions
    }
}

FI R ST FI R ST

I think that you only declared your mocks but never defined/created them.我认为您只声明了您的模拟,但从未定义/创建它们。 There's basically three ways to do so:基本上有三种方法可以做到这一点:

1) mockito extension 1) mockito 扩展

The convenient way in JUnit 5 is to use the MockitoExtension: JUnit 5 中的便捷方法是使用 MockitoExtension:

@ExtendWith(MockitoExtension.class)
public class LabelServiceImplTest {

  // declare mocks, spies and captors ...

  // system under test
  @InjectMocks
  LabelService sut;

}

Nothing else should be needed now.现在不需要其他任何东西。

Mind that by @InjectMocks you instruct Mockito to create your system under test and inject the mocks.请注意,通过@InjectMocks ,您可以指示 Mockito 创建您的测试系统并注入模拟。 You may as well do that manually, eg by injecting mocks into the SUT's constructor.您也可以手动执行此操作,例如通过将模拟注入 SUT 的构造函数。 I prefer that way to have more control over the configuration of my SUT.我更喜欢这种方式来更好地控制我的 SUT 的配置。

2) MockitoAnnotations.openMocks(testclass) 2) MockitoAnnotations.openMocks(testclass)

This method will inject mocks into the given object:此方法会将模拟注入给定的 object:

public class LabelServiceImplTest {

  // declare mocks, spies and captors ...

  // system under test
  @InjectMocks
  LabelService sut;

  @BeforeEach
  void setup() {
    MockitoAnnotations.openMock(this)
    
    // ...
  }

}

On JUnit 4 you'd use MockitoAnnotations.initMocks(…) instead.在 JUnit 4 上,您将使用MockitoAnnotations.initMocks(…)代替。

3) the old school way 3)老派的方式

public class LabelServiceImplTest {

  LabelRepository labelRepositoryMock = Mockito.mock(LabelRepository.class);

  // ...

  // system under test
  LabelServiceImpl sut;


  @BeforeEach
  void setup() {
    sut = new LabelService(labelRepositoryMock);

    // ...
  }

}

SE C OND SE C OND

Your questions …你的问题 …

1) Do you need to create the request and the label? 1) 您是否需要创建请求和 label?

Yes.是的。 You call the service using the request and the mock returns the label.您使用请求调用服务,然后模拟返回 label。 I would suggest you do not use the request's property to set up your label, use a String literal instead.我建议您不要使用请求的属性来设置您的 label,而是使用String文字。

2) Do you need to mock the saveAndFlush() method? 2) 你需要模拟 saveAndFlush() 方法吗?

Yes.是的。 This is the part where you configure your SUT's environment to behave in a predictable way.这是您将 SUT 的环境配置为以可预测的方式运行的部分。


You might consider postfixing your mocks, spies and captors by: "Mock", "Spy" and "Captor".你可以考虑通过“Mock”、“Spy”和“Captor”来对你的模拟、间谍和俘虏进行后缀。 You might also consider calling your system under test "sut".您也可以考虑将您的测试系统称为“sut”。

@Mock
private LabelRepository labelRepositoryMock;

@Captor
ArgumentCaptor<Label> labelCaptor;

// system under test
@InjectMocks
private LabelServiceImpl sut;
    

This makes the test code more readably, I think.我认为这使测试代码更具可读性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM