Вступление
В этом посте я хотел бы показать пример использования правила JUnit для упрощения тестирования.
Недавно я унаследовал довольно сложную систему, которая не все проверена. И даже проверенный код сложен. В основном я вижу отсутствие тестовой изоляции. (Я напишу другой блог о работе с Legacy Code).
Один из тестов (и кода), который я исправляю, фактически тестирует несколько компонентов вместе. Он также подключен к БД. Он проверяет некоторую логику и пересечение между компонентами. Когда код не компилировался в совершенно другом месте, тест не мог быть запущен, поскольку он загружал весь контекст Spring. Структура была такова, что перед тестированием (любого класса) был инициирован весь Spring-контекст. Тесты расширяют BaseTest, который загружает весь контекст Spring.
BaseTest также очищает БД в методе @After.
Важное примечание: эта статья посвящена изменению тестов, которые не являются полностью корректными. При создании нового кода и тестов они должны быть изолированы, тестировать одну вещь и т. Д. Лучшие тесты должны использовать фиктивные БД / зависимости и т. Д. После того, как я исправлю тест и рефакторинг, я буду уверен, что внесу больше изменений.
Вернуться к нашей теме …
Итак, что я получил, так это медленный запуск тестового набора, отсутствие изоляции и даже проблемы с запуском тестов из-за не связанных проблем. Поэтому я решил отделить загрузку контекста от соединения с БД и от того и другого до очистки базы данных.
Подход
Чтобы добиться этого, я сделал три вещи: во-первых, изменил наследование тестового класса. Он перестал наследовать BaseTest. Вместо этого он наследует AbstractJUnit4SpringContextTests. Теперь я могу создать свой собственный контекст для каждого теста и не загружать все.
Теперь мне нужно было два правила: @ClassRule и @Rule @ClassRule будут отвечать за соединение с БД @Rule будет очищать БД после / перед каждым тестом.
Но сначала, что такое JUnit Rules?
Краткое объяснение состоит в том, что они дают возможность перехватить метод испытания, аналогичный концепции АОП. @Rule позволяет нам перехватывать метод до и после фактического запуска метода. @ClassRule перехватывает тестовый прогон класса. Очень известная @Rule — это TemporaryFolder от JUnit.
(Аналогично @Before, @After и @BeforeClass).
Создание @Rule
Самым простым было создать правило, которое очищает БД до и после тестового метода. Вам необходимо реализовать TestRule , у которого есть один метод: Statement apply (База операторов , Описание описания); Вы можете многое сделать с этим. Я узнал, что обычно у меня будет внутренний класс, который расширяет Statement . Созданное мной правило не создает соединение с БД, но получает его в конструкторе.
Вот полный код:
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
|
public class DbCleanupRule implements TestRule { private final DbConnectionManager connection; public MongoCleanupRule(DbConnectionManager connection) { this .connection = connection; } @Override public Statement apply(Statement base, Description description) { return new MongoCleanupStatement(base, connection); } private static final class DbCleanupStatement extends Statement { private final Statement base; private final DbConnectionManager connection; private MongoCleanupStatement(Statement base, DbConnectionManager connection) { this .base = base; this .connection = connection; } @Override public void evaluate() throws Throwable { try { cleanDb(); base.evaluate(); } finally { cleanDb(); } } private void cleanDb() { connection.doTheCleanup(); } } } |
Создание @ClassRule
ClassRule на самом деле также TestRule. Единственное отличие от правила состоит в том, как мы используем его в нашем тестовом коде. Я покажу это ниже.
Сложность в создании этого правила заключалась в том, что я хотел использовать контекст Spring, чтобы получить правильное соединение.
Вот код:
(ExternalResource — TestRule)
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
|
public class DbConnectionRule extends ExternalResource { private DbConnectionManager connection; public DbConnectionRule() { } @Override protected void before() throws Throwable { ClassPathXmlApplicationContext ctx = null ; try { ctx = new ClassPathXmlApplicationContext( "/META-INF/my-db-connection-TEST-ctx.xml" ); mongoDb = (DbConnectionManager) ctx.getBean( "myDbConnection" ); } finally { if (ctx != null ) { ctx.close(); } } } @Override protected void after() { } public DbConnectionManager getDbConnecttion() { return connection; } } |
(Вы видели, что я могу заставить DbCleanupRule наследовать ExternalResource?)
Используй это
Последняя часть о том, как мы используем правила. @Rule должно быть открытым полем. @ClassRule должен быть открытым статическим полем.
И вот оно:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@ContextConfiguration (locations = { "/META-INF/one-dao-TEST-ctx.xml" , "/META-INF/two-TEST-ctx.xml" }) public class ExampleDaoTest extends AbstractJUnit4SpringContextTests { @ClassRule public static DbCleanupRule connectionRule = new DbCleanupRule (); @Rule public DbCleanupRule dbCleanupRule = new DbCleanupRule(connectionRule.getDbConnecttion()); @Autowired private ExampleDao classToTest; @Test public void foo() { } } |
Это все.
Надеюсь, поможет.
Ссылка: | Правила JUnit от нашего партнера по JCG Эяля Голана в блоге « Обучение и совершенствование в качестве мастера» . |