Я решил немножко упростить такой подход и вынести установку предусловий на уровень декларации (то бишь в нашем случае это будут аннотации). Пример, который буду здесь рассматривать не имеет ничего общего с тем кодом, с которым приходилось работать, т.к. он довольно сложный для демонстрации самой идеи.
Итак, предположим нам нужно разработать фильтр для файлов, аналогичный тому, который мы используем в файловых менеджерах. Например если наложить фильтр *.txt, то должен вернутся список текстовых файлов.
Понятно, что тестовых сценариев может быть масса и в обычном случае мы бы создавали временную папку в setUp, удаляли ее в tearDown. В каждом тесте создавали бы файлики с разными именами, после чего проверяли бы фильтр.
А было бы легче работать с тестом, который написан таким образом?
Как такое может заработать?
Самый простой вариант - это реализовать свой ранер, наследник от базового класса org.junit.runner.Runner и указывать его в качества "запускальщика":
Полный текст кода класса FileFilterRunner
Что здесь интересного
1. Наследуемся от BlockJUnit4ClassRunner
2. Перекрыт метод runChild. В нем перед вызовом тестового метода создаем временную директорию и заполняем ее тестовыми файлами, имена которых берем из нашей аннотации Given. Для работы с файлами использую библиотеку Apache Commons IO
3. Перекрыт метод validatePublicVoidNoArgMethods. Это позволяет запускать тестовые методы с параметрами
4. Перекрыт метод methodInvoker. Этот метод вызывается JUnit фреймворком для каждого тестового метода
5. Класс ParameterizedInvoker - это наша реализация "вызывальщика" тестовых методов. Мы вызываем тестовый метод с параметром testFolder
Вот сама аннотация Given:
Что получили?
1. Тест стал более читабелным за счет того, что мы вынесли инициализацию предусловий в документирующую аннотацию Given.
2. Реализованный ранер можно легко использовать в других тестах.
3. Если развивать этот подход, то уменьшится необходимость создавать суперкласс для тестовых классов, в котором хранить общую логику для всех наследуемых тестов. Все в аннотациях.
4. Нету setUp/tearDown в тесте
5. Инициализируем только то, что нужно для теста. Если нам, например, понадобится набор папок, то мы добавим в аннотацию параметер folders. Если пользователь с именем "Вася", то - параметр users.
6. Можем ужесточать или ослаблять предусловия. Что если, например, параметр files станет обязательным? Мы уберем инициализацию по умолчанию default {}, тем самым тесты, которые не использовали обязательные параметры перестанут компилироваться и мы их сразу обнаружим
7. Если видишь, чем хорош или плох этот подход - пиши комент, дополню список
Идеи развития
Аннотированные параметры тестового метода. Если есть необходимость передавать разные параметры в тестовый метод, то мы можем их именовать с помощью анотаций, имеющих тип @Target(ElementType.PARAMETER):
Например, нам в тесте понадобился список созданных файлов и путь к корневой директории:
Полный текст исходников и проекта находится здесь качать
No comments:
Post a Comment