简体   繁体   English

ArgumentCaptor 在单元测试中的使用

[英]ArgumentCaptor usage in Unit Tests

I am trying to create a Unit Test for the following service method:我正在尝试为以下服务方法创建单元测试:

public CompanyDTO update(CompanyRequest companyRequest, UUID uuid) {

    final Company company = companyRepository.findByUuid(uuid)
            .orElseThrow(() -> new EntityNotFoundException("Not found"));            
    
    company.setName(companyRequest.getName());

    final Company saved = companyRepository.save(company);
    return new CompanyDTO(saved);
}

I created the following Unit Test:我创建了以下单元测试:

@InjectMocks
private CompanyServiceImpl companyService;

@Mock
private CompanyRepository companyRepository;

@Captor
ArgumentCaptor<Company> captor;



@Test
public void testUpdate() {
    final Company company = new Company();
    company.setName("Company Name");

    final UUID uuid = UUID.randomUUID();
    final CompanyRequest request = new CompanyRequest();
    request.setName("Updated Company Name");
  
    when(companyRepository.findByUuid(uuid))
        .thenReturn(Optional.ofNullable(company));        
    when(companyRepository.save(company)).thenReturn(company);

    CompanyDTO result = companyService.update(request, uuid);

    /* here we get the "company" parameter value that we send to save method 
    in the service. However, the name value of this paremeter is already 
    changed before passing to save method. So, how can I check if the old 
    and updated name value? */
    Mockito.verify(companyRepository).save(captor.capture());

    Company savedCompany = captor.getValue();

    assertEquals(request.getName(), savedCompany.getName());
}

As far as I know, we use ArgumentCaptor to catch the value we pass to a method.据我所知,我们使用ArgumentCaptor来捕获传递给方法的值。 In this example, I need to catch the value at correct time and compare the updated value of name sent to the update method and the returned value of name property after update.在这个例子中,我需要在正确的时间捕获值,并将发送到 update 方法的 name 的更新值与 update 后 name 属性的返回值进行比较。 However, I cannot find how to test it properly and add necessary comment to my test method.但是,我找不到如何正确测试它并向我的测试方法添加必要的注释。 So, how should I use ArgumentCaptor to verify my update method updates the company with the given value ("Updated Company Name").那么,我应该如何使用ArgumentCaptor来验证我的更新方法使用给定值(“更新的公司名称”)更新公司。

Here's your scenario for an ArgumentCaptor .这是ArgumentCaptor的场景。

Code under test:被测代码:

public void createProduct(String id) {
  Product product = new Product(id);
  repository.save(product);
}

Note that a) we create an object within the tested code, b) we want to verify something specific with that object, and c) we don't have access to that object after the call.请注意,a) 我们在测试代码中创建了一个对象,b) 我们想要验证该对象的特定内容,以及 c) 在调用后我们无权访问该对象。 Those are pretty much the exact circumstances where you need a captor.这些几乎就是您需要俘虏的确切情况。

You can test it like this:你可以这样测试:

void testCreate() {
  final String id = "id";
  ArgumentCaptor<Product> captor = ArgumentCaptor.for(Product.class);

  sut.createProduct(id);

  // verify a product was saved and capture it
  verify(repository).save(captor.capture());
  final Product created = captor.getValue();

  // verify that the saved product which was captured was created correctly
  assertThat(created.getId(), is(id));
}

Here's how I would write this test case, with explanations as code comments.下面是我将如何编写此测试用例,并将解释作为代码注释。

@Test
public void testUpdate() {
    // we make this a mock so we can analyze it later
    final Company company = mock(Company.class);

    // no need to have the same String literal twice
    final String updatedName = "Updated Company Name";

    // we make this a mock because only that one method is used
    // if you create an actual object, you'll need to adjust this test 
    // when the constructor changes later
    final CompanyRequest request = mock(CompanyRequest.class);
    when(request.getName()).thenReturn(updatedName);
  
    // we don't really care about the actual uuid, so any() will do here
    when(companyRepository.findByUuid(any()))
        .thenReturn(Optional.ofNullable(company));
    when(companyRepository.save(company)).thenReturn(company);

    // setup complete, let's go
    CompanyDTO result = companyService.update(request, UUID.randomUUID());

    // we want to make sure the name change has been applied
    Mockito.verify(company.setName(udpatedName);
    // make sure it has been saved as well
    Mockito.verify(companyRepository).save(company);

    // verify that the returned dto actually contains the company we've been working on
    Company savedCompany = result.getCompany();
    assertSame(savedCompany, company);
}

Note that this does not answer your actual question about how to use an argument captor, but that's because that was an XY problem.请注意,这并不能回答您关于如何使用参数捕获器的实际问题,但那是因为这是一个 XY 问题。

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

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