barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
Показаны сообщения с ярлыком unit-testing. Показать все сообщения
Показаны сообщения с ярлыком unit-testing. Показать все сообщения

пятница, 18 октября 2019 г.

Mockito: проверка, что метод был вызван несколько раз с определенными аргументами

Задача

Есть некий мок-объект, нужно проверить, что некоторый его метод был вызван дважды, причем с определенными значениями аргументов.

Решение

Тут поможет argument capturing:
ArgumentCaptor<String> argsCaptor = ArgumentCaptor.forClass(String.class);
verify(myMockObj, atLeastOnce()).someMethod(argsCaptor .capture());
List<String> args= argsCaptor.getAllValues();
assertEquals(2, argsCaptor.size());       
assertTrue(argsCaptor.contains("arg1));
assertTrue(argsCaptor.contains("arg2));

суббота, 23 марта 2013 г.

HTTP server stubbing

Нашел неплохой вариант для стаббинга HTTP-сервера, позволяющий автоматизировать тестирование HTTP-клиентов. Называется сабж Wiremock, имеет неплохую документацию с примерами. Есть в репозитории мавена и подключается к проекту так:
<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> <version>1.30</version> <scope>test</scope> </dependency>
Использовать его просто (по крайней мере, в тривиальных тестах), ниже приведен пример теста, проверяющего реакцию некоторого тестируемого класса на коды ответа HTTP-сервера 200 и 404:
import static com.github.tomakehurst.wiremock.client.WireMock.*;
...
private static final int STUB_PORT = 17777;
@Rule
public WireMockRule wireMockRule = new WireMockRule(STUB_PORT);

@Test
void someTest() {
 stubFor(get(urlEqualTo("/200"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/html")
.withBody("200 OK")));
// дальше некий код, запрашивающий "http://localhost:"+STUB_PORT+"/200"
stubFor(get(urlEqualTo("/404"))
.willReturn(aResponse()
.withStatus(404)
.withHeader("Content-Type", "text/html")
.withBody("404 Not Found")));
// дальше некий код, запрашивающий "http://localhost:"+STUB_PORT+"/404"
}
Проект правда неидеален, например, проверить basic-аутентификацию у меня так не получилось, метод verify почему-то игнорирует требование наличия заголовка "Authorization". Хотя я, честно говоря, особо не заморачивался с этой проблемой, возможно она решаема и без копания в исходниках Wiremock`а (которые, кстати, доступны на гитхабе: https://github.com/tomakehurst/wiremock).

пятница, 22 марта 2013 г.

Maven и unit-тестирование классов, использующих servlet-api

Проблема:

Имеется maven-проект веб-приложения, у него есть следующая зависимость:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
Есть некий класс, использующий servlet-api (расширующий HttpServlet). Для него написан unit-тест, при попытке выполнить который валится ошибка:
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; nested exception is java.lang.reflect.InvocationTargetException: null
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
Caused by: java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/http/HttpServlet
at java.lang.ClassLoader.defineClass1(Native Method)
...
Причина:

javaee-web-api не может быть использован при выполнении java-кода, т.к. не содержит тел методов.

Решение:

Добавить test-зависимость, содержащую имплементацию servlet-api, например:
<dependency>
<groupId>tomcat</groupId>
<artifactId>servlet</artifactId>
<version>4.1.36</version>
<scope>test</scope>
</dependency>

среда, 16 января 2013 г.

вторник, 15 января 2013 г.

Maven: создание тестового окружения для тестирования war-проекта, использующего overlays при сборке

Задача:

Есть проект, собираемый maven`ом, а точнее, maven-war-plugin`ом. У проекта есть несколько zip-зависимостей с ресурсами, распаковываемыми в WEB-INF/classes при создании war`ника (при помощи overlays). Задача состоит в том, чтобы при запуске unit-тестов также распаковывать эти зависимости куда-нибудь в тестовый classpath (например, в target/test-classes), чтобы для тестов формировалось окружение, аналогичное тому, что будет после запаковки в war.

Решение:

Для копирования ресурсов для тестов (и не только), можно воспользоваться maven-resources-plugin`ом. Вот только он умеет копировать файлы только из папки А в папку В, и про зависимости ничего не знает. Для работы с зависимостями есть плагин maven-dependency-plugin, а для распаковки ресурсов из зависимостей подойдет его гол "unpack". В моем случае он должен отрабатывать на фазе "process-test-resources" (когда тестовые классы уже скомпилированы и помещены в ${basedir}/target/test-classes). В итоге фрагмент pom`ки, отвечающий за распаковку ресурсов из zip-зависимостей, выглядит так:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
 <execution>
<id>copy-war-or-zip-resources-for-test</id>
<phase>process-test-resources</phase>
<goals>
 <goal>unpack</goal>
</goals>
<configuration>
 <outputDirectory>${basedir}/target/test-classes</outputDirectory>
 <artifactItems>
<artifactItem>
 <groupId>my.dep.group.id</groupId>
 <artifactId>artifact-id</artifactId>
 <classifier>resources</classifier>
 <type>zip</type>
</artifactItem>
<!-- ... -->
 </artifactItems>
 <excludes>my-exluded.properties</excludes>  
</configuration>
 </execution>
</executions>
</plugin>    
Правда, гибкость в данном случае, по сравнению с overlays, теряется, т.к. overlays позволяет указывать исключения для каждой зависимости, а  unpack - только для всех сразу.
PS А maven-resources-plugin пойдет для использования в качестве замены webResources в maven-war-plugin. Использовать его нужно на той же фазе process-test-resources.

пятница, 24 июня 2011 г.

Шаблон проектирования ObjectMother для написания (модульных) тестов

Вместо:
- тестируемые, а также все остальные необходимые для теста объекты, создаются и инициализируются в коде самого теста (если нет возможности объединить общую фикстуру, инициализируемую в setUp-методе)
- тестируемые, а также все остальные необходимые для теста объекты, создаются и инициализируются в отдельных методах тест-кейса, обращение к которым производится из различных тестов, которым необхоима аналогичная инициализация

Предлагается:
- использование одного или нескольких ObjectMother - классов, которые содержат методы для создания и(или) модификации любых часто используемых в тестовых методах объектов в различных комбинациях. Методы этого класса должны содержать максимально говорящие об их назначении имена, чтобы понятность тестов не снижалась. Объекты, создаваемые ObjectMother, должны иметь возможность легко менять свое состояние, так чтобы после их инициализации в ObjectMother, тесты могли легко поменять его (состояние) на требуемое. Чаще всего ObjectMother создает обычные доменные объекты вместе с требуемыми атрибутами, намного реже - заглушки и мок-объекты, так как последние более специфичны для конкретных тестов и менее - для всего набора модульных тестов приложения.

"+": повышается читабельность кода тестов, уменьшается дублирование инициализирующего кода, упрощается создание новых тестов

Более подробно и с примерами есть тут: http://wiki.agiledev.ru/doku.php?id=tdd:object_mother

четверг, 23 июня 2011 г.

Шаблон проектирования Inversion of Control (IOC)

Вместо:
- класс сам создает экземпляры сторонних классов (ресурсов), от которых он зависит
предлагается:
- класс получает объекты этих классов (ресурсов) одним из следующих способов: как параметр конструктора / setter-метод / параметр того метода, где эти объекты непосредственно нужны

"+": большая гибкость при использовании класса (особенно когда тип передаваемых объектов задается не конкретным классом, а интерфейсом)
"+": упрощается unit-тестирование. Можно контролировать состояние передаваемых классу объектов непосредственно в коде самого текста, а также заменять их мок-объектами