The other day, when I learned about unit tests, I sometimes wanted to verify the arguments passed to a method, and I used ArgumentCaptor at that time, so I will write an article.
During unit testing, for example, you may want to verify that a stubbed method was called properly, or that arguments were passed properly for that method. That's when I used ArgumentCaptor.
-First, describe the case where one argument is passed to the method of the mocked object. ・ First, I will describe the class to be tested.
PersonItemWriter.java
public class PersonItemWriter implements ItemWriter<Object>{
@Autowired
PersonService service;
@Override
public void write(List<? extends Object> items) throws Exception {
for(Object person : items) {
service.updatePerson((Person)person);
}
}
}
-This class was introduced a little in "Summary of what I learned in Spring Batch", which I implemented for the Writer class. -It is a simple process to register the list of objects passed as parameters in the database using the service class. -In this test, this class is tested, and at that time, PersonService is not completed yet, so it is set to mock. -Assuming that the Person Service is as follows.
PersonService.java
public interface PersonService {
//Process to register in the database
public void updatePerson(Person person);
}
・ The following is a test class.
PersonItemWriterTest.java
public class PersonItemWriterTest {
@Mock
PersonService personService;
@InjectMocks
private PersonItemWriter writer = new PersonItemWriter();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testParameter() throws Exception {
ArgumentCaptor<Person> personCaptor = ArgumentCaptor.forClass(Person.class);
List<Person> people = new ArrayList<Person>();
for(int i = 0; i < 3; i++) {
Person person = new Person(i, "hoge" + i, "fuga" + i);
people.add(person);
}
//Execution of the test target
writer.write(people);
verify(personService, times(3)).updatePerson(personCaptor.capture());
Person person = personCaptor.getValue();
assertThat(person, is(people.get(people.size() - 1)));
}
}
-Person Service is mocked using Mock annotation (For how to use mockito, refer to the site described in "Reference"). -When verifying the arguments, prepare an instance of ArgumentCaptor. -You can specify the class when you make an instance, but in that case, specify the class that the parameter of the method you want to test can take. -In this test, I want to verify whether PersonService.updatePerson () was called properly. -Therefore, the class specified for ArgumentCaptor is Person. -The method that verifies whether the method has been called is Mockito's verify (). -Pass ArgumentCaptor as an argument of the method specified by verify. · Verify if PersonItemWriter.write () called PersonService.updatePerson () with verify, and use ArgumentCaptor.getValue () to verify if the arguments were correct.
-I think that there may be multiple arguments of the method for which you want to verify whether it has been executed. -For example, when the PersonService method is as follows.
PersonService.java
public interface PersonService {
public void updatePersonById(int id, String lastName, String firstName);
}
-PersonItemWriter uses PersonService as follows.
PersonItemWriter.java
public class PersonItemWriter implements ItemWriter<Object>{
@Autowired
PersonService service;
@Override
public void write(List<? extends Object> items) throws Exception {
for(Object person : items) {
service.updatePersonById(((Person) person).getId(), ((Person) person).getLastName(), ((Person) person).getFirstName());
}
}
}
-If you want to verify the argument passed to PersonService.updatePersonById () when validating PersonItemWriter.write (), write the test as follows.
PersonItemWriterTest.java
public class PersonItemWriterTest {
@Mock
PersonService personService;
@InjectMocks
private PersonItemWriter writer = new PersonItemWriter();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testParameter() throws Exception {
ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<String> lastNameCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> firstNameCaptor = ArgumentCaptor.forClass(String.class);
List<Person> people = new ArrayList<Person>();
for(int i = 0; i < 3; i++) {
Person person = new Person(i, "hoge" + i, "fuga" + i);
people.add(person);
}
//Execution of the test target
writer.write(people);
verify(personService, times(3)).updatePersonById(idCaptor.capture(), lastNameCaptor.capture(), firstNameCaptor.capture());
Integer id = idCaptor.getValue();
String lastName = lastNameCaptor.getValue();
String firstName = firstNameCaptor.getValue();
assertThat(id, is(people.get(people.size() - 1).getId()));
assertThat(lastName, is(people.get(people.size() - 1).getLastName()));
assertThat(firstName, is(people.get(people.size() - 1).getFirstName()));
}
}
-Prepare multiple ArgumentCaptors for multiple arguments. -Since the class set in ArgumentCaptor must be a "reference type" class, if a primitive type is taken as an argument, set a wrapper class (here, Integer for int). -As with the case where the argument is singular, pass ArgumentCaptor.capture () to each argument as an argument when verifying the method with verify (). -Finally, the value is obtained by getValue () from each of the three ArgumentCaptors passed to the argument, and the value passed to the argument is verified.
This concludes the explanation of how to verify arguments using ArgumentCaptor.
reference: [Argument verification by ArgumentCaptor](https://riptutorial.com/ja/mockito/example/16192/argumentcaptor argument verification) Beginning of Mockito How to use verify of Junit library "Mockito" Too comfortable mock library Mockito ~ Part 1 ~ JUnit memorandum
Recommended Posts