В этой статье мы увидим JUnit и Mockito в действии, используя 2 компонента Java, класс сервиса и класс репозитория.
Мы начнем с создания классов, а затем напишем тесты различными способами, чтобы использовать такие понятия, как assert, verify, check for throwned exception, ArgumentMatcher и ArgumentCaptor. Наконец, мы создадим более чистые тесты, извлекая дублированный код и даже используя Mockitoannotations. Мы не будем фокусироваться на 100% покрытии кода.
Тестируемый код DataRepository.java
public interface DataRepository {
int[] retrieveAllData();
int getStoredSumById(int id);
void save(Object o);
}
DataService.java
public interface DataService {
int calculateSum();
void setDataRepository(DataRepository dataRepository);
int calculateNewSum(int id);
void save(Data o);
}
DataServiceImpl.java
public class DataServiceImpl implements DataService {
private DataRepository dataRepository;
public void setDataRepository(DataRepository dataRepository) {
this.dataRepository = dataRepository;
}
public int calculateSum(){
int sum = 0;
for(int value : dataRepository.retrieveAllData()){
sum += value;
}
return sum;
}
public int calculateNewSum(int id){
int sum = dataRepository.getStoredSumById(id);
return sum + sum;
}
public void save(Data o){
o = new Data(o.getName().toUpperCase());
dataRepository.save(o);
}
}
Data.java
public class Data {
private String name;
// constructors, getters and setters
}
Модульные тесты без аннотаций Mockito Проверяют метод calculateSum() из класса service, mocking repository. В этом тесте мы предполагаем, что имитируемый метод извлечения всех данных() из репозитория возвращает массив с данными.
@Test
public void calculateSum_Should_ReturnResult_When_DataIsProvided() {
//create service under test
DataService ms = new DataServiceImpl();
//mock repository to test service in isolation
DataRepository dataRepositoryMock = mock(DataRepository.class);
when(dataRepositoryMock.retrieveAllData()).thenReturn(new int[]{1, 2, 3});
//set mock to service
ms.setDataRepository(dataRepositoryMock);
//call method under test
int result = ms.calculateSum();
//verify if method on the mock is called by service under test
//it is mostly used when a method that is called on a mock does not have a return
verify(dataRepositoryMock, times(1)).retrieveAllData();
//assert result
assertEquals(6, result);
}
Тест для метода calculateSum() из класса service, имитирующего репозиторий. Мы предполагаем, что издевательский метод извлечения всех данных() из репозитория возвращает массив без данных.
@Test
public void calculateSum_Should_ReturnZero_When_DataIsEmpty() {
//create service under test
DataService ms = new DataServiceImpl();
//mock repository to test service in isolation
DataRepository dataRepositoryMock = mock(DataRepository.class);
when(dataRepositoryMock.retrieveAllData()).thenReturn(new int[]{});
//set mock to service
ms.setDataRepository(dataRepositoryMock);
//call method under test
int result = ms.calculateSum();
//verify if method on the mock is called by service under test
verify(dataRepositoryMock, times(1)).retrieveAllData();
//assert result
assertEquals(0, result);
}
Тест для метода calculateSum() из класса service, имитирующего репозиторий. Предполагая, что издевательский метод retrieveAllData() из репозитория возвращает значение null, в результате чего метод calculateSum() из класса service выдает исключение NullPointerException.
@Test
public void calculateSum_Should_ThrowException_When_DataIsNull() {
assertThrows(NullPointerException.class, () -> {
//create service under test
DataService ms = new DataServiceImpl();
//mock repository to test service in isolation
DataRepository dataRepositoryMock = mock(DataRepository.class);
when(dataRepositoryMock.retrieveAllData()).thenReturn(null);
//set mock to service
ms.setDataRepository(dataRepositoryMock);
//call method under test
ms.calculateSum();
});
}
Тест для метода calculate New Sum() из класса service, имитирующего репозиторий с использованием ArgumentMatchers. В этом тесте мы предполагаем, что издевательский метод getStoredSumById() из репозитория возвращает 2. Новая тема, представленная здесь, – это сопоставители аргументов, такие как any(), any Int() и т.д. это может быть использовано для замены фактического аргумента, чтобы сделать тесты более универсальными.
@Test
void calculateNewSum_Should_ReturnResult_When_DataIsProvided() {
//create service under test
DataService ms = new DataServiceImpl();
//mock repository to test service in isolation
DataRepository dataRepositoryMock = mock(DataRepository.class);
//return 2 when method is called with any int value
when(dataRepositoryMock.getStoredSumById(anyInt())).thenReturn(2);
//set mock to service
ms.setDataRepository(dataRepositoryMock);
//call method under test
int result = ms.calculateNewSum(1);
//verify if method on the mock is called by service under test with any argument
verify(dataRepositoryMock, times(1)).getStoredSumById(anyInt());
//assert result
assertEquals(4, result);
}
Тест на метод save() из класса service, имитирующий репозиторий. В этом случае метод save() из класса service не возвращает ничего, что должно быть утверждено. Мы можем использовать ArgumentCaptor, чтобы проверить, вызывается ли метод save() из класса репозитория с ожидаемыми аргументами.
@Test
void save_ShouldCallRepository_With_GivenParam() {
// create service under test
DataService ms = new DataServiceImpl();
// mock repository to test service in isolation
DataRepository dataRepositoryMock = mock(DataRepository.class);
// set mock to service
ms.setDataRepository(dataRepositoryMock);
// call method under test
Data o = new Data("MockitoObject");
ms.save(o);
//create expected object
Data expected = new Data("MOCKITOOBJECT");
// because the method does not return anything we can check
// if mock method was called with an expected parameter
ArgumentCaptor captor = ArgumentCaptor.forClass(Data.class);
verify(dataRepositoryMock, times(1)).save(captor.capture());
//assert captured argument
assertEquals(expected, captor.getValue());
}
Вся статья была опубликована по адресу https://weinspire.tech
Оригинал: “https://dev.to/gnvoicu/jumpstart-testing-with-mockito-and-junit5-13e8”