[JAVA] Validate arguments using ArgumentCaptor with mockito

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.

Overview

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.

When the number of arguments is one

-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.

When there are multiple arguments

-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

Validate arguments using ArgumentCaptor with mockito
Using Java with AWS Lambda-Implementation-Check CloudWatch Arguments
Using Pair with OpenJDK
Precautions when using Mockito.anyString as an argument when Mocking with Mockito
Try using GloVe with Deeplearning4j
Try using view_component with rails
Japaneseize using i18n with Rails
Using Mapper with Java (Spring)
UnitTest with SpringBoot + JUnit + Mockito
Validate path parameters with @RequestMapping
Using PlantUml with Honkit [Docker]
Java unit tests with Mockito