Возможность модульного тестирования компонентов пружины без необходимости загрузки полного контекста пружины с его специальными конфигурациями тестирования, на мой взгляд, является большим преимуществом, поскольку она чиста, проста в обслуживании, быстрее пишется, плавно изменяется ,
Чтобы достичь этой цели, используйте Mockito и попросите его заменить компоненты @Autowired в классе, который вы хотите протестировать, на Mocks (или Spies).
Вот пример.
У нас есть сервис под названием SalaryService, который угадает что, вычисляет гипотетическую чистую зарплату на основе переданного идентификатора сотрудника. Простая концепция.
Служба требовала, чтобы сотрудники, один из которых, EmployeeDAO, получал брутто-зарплату, а второй, TaxCalculator, применял некоторые налоги на основе брутто-зарплаты.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.marco.springmockito; import java.math.BigDecimal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class SalaryService { private static final BigDecimal minimumSalary = new BigDecimal( 20000 ); @Autowired private EmployeeDAO employeeDAO; @Autowired private TaxCalculator taxCalculator; public BigDecimal getNetSalary( long employeeId) { BigDecimal netSalary = null ; BigDecimal grossSalary = employeeDAO.getAnnualSalary(employeeId); BigDecimal taxes = taxCalculator.calculateTaxes(grossSalary); if (taxedSalaryIsGreaterThanMinimumSalary(grossSalary)) { netSalary = grossSalary.subtract(taxes); } else { netSalary = grossSalary; } return netSalary; } private boolean taxedSalaryIsGreaterThanMinimumSalary(BigDecimal taxedSalary) { return taxedSalary.compareTo(minimumSalary) == 1 ; } } |
EmployeeDAO — это классическая служба, которая отвечает за извлечение информации из постоянного хранилища, и будет выглядеть примерно так.
01
02
03
04
05
06
07
08
09
10
11
12
|
package com.marco.springmockito; import java.math.BigDecimal; import org.springframework.stereotype.Component; @Component public class EmployeeDAO { public BigDecimal getAnnualSalary( long employeeId) { // conncetTODB // run select for employeeId; return new BigDecimal( 70000 ); } } |
TaxCalculator понадобится TaxDao для получения информации о налогах, а затем он будет выполнять какие-то скучные и долгие вычисления для возврата налогов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
package com.marco.springmockito; import java.math.BigDecimal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TaxCalculator { @Autowired private TaxDao taxDao; public BigDecimal calculateTaxes(BigDecimal salary) { BigDecimal result = salary.multiply(taxDao.getTaxPercentageForYear( 2014 )); // some other weird calculation .... return result; } } |
Теперь мы хотим провести модульное тестирование класса SalaryService. Нас не должны беспокоить какие-либо настройки DAO и баз данных. В этом тесте UNIT нам все равно, что делает TaxCalculator.
Мы хотим проверить, работает ли наш SalaryService так, как ожидается, и способен ли он правильно использовать работу своих сотрудников.
Вот как мы это делаем с Mockito. Мы помечаем класс, который мы хотим протестировать, с помощью @InjectMocks и помечаем @Mock всех его соавторов (или @Spy, если вам нужна реальная реализация).
Наконец, мы должны сообщить нашей структуре блока, чтобы выполнить необходимые инъекции Mockito перед началом теста, и мы делаем это с
MockitoAnnotations. initMocks (это) ;.
В тесте нам нужно смоделировать ожидаемые операции, чтобы мы могли сосредоточиться на реальной логике, которую мы хотим проверить внутри SalaryService.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package com.marco.springmockito; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; import java.math.BigDecimal; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class SalaryServiceTest { private static final long UserId = 123l; @InjectMocks private SalaryService salaryService; @Mock private EmployeeDAO employeeDAO; @Mock private TaxCalculator taxCalculator; @Before public void init() { MockitoAnnotations.initMocks( this ); } @Test public void testMinimumSalary() { BigDecimal annualSalary = new BigDecimal( 10000 ); when(employeeDAO.getAnnualSalary(UserId)).thenReturn(annualSalary); when(taxCalculator.calculateTaxes(annualSalary)).thenReturn( new BigDecimal( 1000 )); BigDecimal actual = salaryService.getNetSalary(UserId); assertThat(actual.compareTo( new BigDecimal( 10000 )), is( 0 )); } @Test public void testMaximumSalary() { BigDecimal annualSalary = new BigDecimal( 80000 ); when(employeeDAO.getAnnualSalary(UserId)).thenReturn(annualSalary); when(taxCalculator.calculateTaxes(annualSalary)).thenReturn( new BigDecimal( 8000 )); BigDecimal actual = salaryService.getNetSalary(UserId); assertThat(actual.compareTo( new BigDecimal( 72000 )), is( 0 )); } } |
Это просто и эффективно, и, надеюсь, это будет полезно для кого-то еще там.