В этом посте я быстро покажу вам, насколько удобны правила JUnit, если вам нужно решить следующую задачу
Метод перехватывает исключение и должен выполнить некоторые дополнительные задачи, прежде чем перебрасывать или выдавать исключение оболочки.
Призывы к дополнительным задачам и выброшенное исключение должны быть проверены модульным тестом.
Это означает, что у вас есть такой код
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class MyThrowingClass { private final ExceptionProcessor exceptionProcessor; public MyThrowingClass(ExceptionProcessor exceptionProcessor) { this.exceptionProcessor = exceptionProcessor; } public void runTask() throws NullPointerException { try { // something to do here throw new NullPointerException("It's null Jim"); } catch (NullPointerException e) { exceptionProcessor.process(e); // This call needs to be verified throw e; } }} |
И позвони в эту линию
|
1
|
exceptionProcessor.process(e); |
Необходимо проверить, а также выброшенное исключение.
Прямо вперед … но некрасиво
Я не буду вдаваться в подробности этого варианта
|
1
2
3
4
5
6
|
try { cut.runMyMethod();} catch(Exception e) { verify(...); assertThat(e).isInstanceOf();} |
поскольку я лично стараюсь избегать попыток использовать конструкции catch в моем тестовом коде, если это возможно.
Первый легкий
Проверить, что исключение выдается, довольно просто, JUnit предоставляет здесь возможные варианты
- Ожидаемый параметр аннотации @Test и
- правило под названием ExceptionRule
Первый вариант будет выглядеть так
|
1
2
3
4
|
@Test(expected = NullPointerException.class)public void myTestWithExpectedParameter() throws Exception { // ...} |
второй такой
|
01
02
03
04
05
06
07
08
09
10
11
|
// ... @Rulepublic ExceptionRule exceptionRule = ExceptionRule.none(); // ... @Testpublic void myTestWithTheExceptionRule() throws Exception { exceptionRule.expect(NullPointerException.class); // ...} |
Нет, это становится немного сложнее
Проблема, стоящая за упомянутым требованием для тестирования, заключается в следующем
Все шаги проверки (…), которые вы выполняете после выполнения тестируемого метода, не будут выполнены, так как исключение останавливает выполнение остальных тестовых методов, как обычно, если исключения генерируются и не перехватываются.
Юнит правил для спасения
С помощью правил JUnit мы можем легко создать способ предоставления дополнительных шагов проверки даже в случае возникновения исключений.
Я знаю, что JUnit уже предоставляет правило верификатора, но я не буду его использовать. Недостатком этого класса является то, что логика проверки записывается в него при настройке.
Итак, нам нужно правило, которое позволяет нам указывать для каждого теста дополнительную логику проверки, которая применяется после выполнения теста.
Общее использование должно выглядеть так
|
01
02
03
04
05
06
07
08
09
10
11
|
@Rulepublic VerifyRule verifyRule = new VerifyRule(); @MockExceptionProcessor exceptionProcessor; @Test()public void working() throws Exception { verifyRule.setVerifier(() -> verify(exceptionProcessor).process(any())); // ..} |
Чтобы это заработало, нам нужны вещи
- VerifyRule
- любой вид интерфейса обратного вызова, который можно установить в правиле проверки
Давайте начнем с интерфейса обратного вызова
|
1
2
3
|
public interface VerifyRuleCallback { void execute() throws Throwable;} |
Здесь нет ничего особенного, как вы можете видеть.
Теперь давайте сосредоточимся на VerifyRule
|
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
|
public class VerifyRule implements TestRule { private VerifyRuleCallback verifyRuleCallback; @Override public Statement apply(Statement base, Description description) { return new VerifyRuleStatement(base); } public void setVerifier(VerifyRuleCallback verifyRuleCallback) { this.verifyRuleCallback = verifyRuleCallback; } private class VerifyRuleStatement extends Statement { private final Statement nextStatement; public VerifyRuleStatement(Statement nextStatement) { this.nextStatement = nextStatement; } @Override public void evaluate() throws Throwable { nextStatement.evaluate(); verifyRuleCallback.execute(); } }} |
Как вы можете видеть, он реализует интерфейс TestRule и предоставляет метод для установки VerifyRuleCallback. Затем обратный вызов используется в методе оценки VerifyRuleStatement, который должен быть реализован для запуска нашей собственной оценки обратного вызова.
Связывая все это вместе
С новым правилом и обратным вызовом тест может выглядеть следующим образом
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class MyThrowingClassShould { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @InjectMocks MyThrowingClass cut; @Mock ExceptionProcessor processor; @Rule public ExpectedException exception = ExpectedException.none(); @Rule public VerifyRule verifyRule = new VerifyRule(); @Test() public void execute_the_exception_processor_and_rethrow_the_exception_when_it_occur() throws Exception { verifyRule.setVerifier(() -> verify(processor).process(any(NullPointerException.class))); exception.expect(NullPointerException.class); cut.runTask(); }} |
Резюме
Как мы видели, правила JUnit предоставляют очень хороший и простой способ создания чистого и понятного тестового кода, когда, и не только в этом случае, возникли требования такого типа при тестировании.
| Ссылка: | Правила JUnit — выполнение дополнительной проверки, когда исключения генерируются нашим партнером JCG Питером Даумом в блоге Coders Kitchen . |