Groovy может значительно снизить уровень работы, связанной с созданием модульных тестов для вашего кода Java. Я обнаружил, что это особенно заметно по количеству строк кода, которые мне нужно написать для создания объектов данных, необходимых для тестов. Я часто заканчиваю тем, что пишу на две сотни строк Java, чтобы создать приличный объем тестовых данных. Используя Groovy, я могу уменьшить это примерно до 50.
В этой статье будет показано, как создать модульный тест в Groovy, который автоматически интегрируется в набор тестов Maven и выполняется при запуске сборки. Я также приведу пример из реальной жизни, чтобы проиллюстрировать, насколько меньше кода требуется при создании вспомогательных данных для теста.
В завершение набора инструментов я также проиллюстрирую, как использовать EasyMock для моделирования зависимостей в тестовом примере Groovy.
Настройка Maven2 для компиляции и запуска ваших тестов Groovy.
Прежде всего, для выполнения тестов нам потребуется настроить конфигурацию Maven2 для использования подключаемого модуля Groovy на этапе тестирования.
Для этого добавьте в файл pom зависимость jar Groovy и назначьте ему правильную область. Добавьте приведенный ниже фрагмент между тегами <dependencies>:
<dependency> <groupId>org.codehaus.mojo.groovy.runtime</groupId> <artifactId>groovy-runtime-1.1</artifactId> <version>1.0-beta-3</version> <scope>test</scope></dependency>
Затем добавьте следующий фрагмент между тегами <build>:
<plugin> <groupId>org.codehaus.mojo.groovy</groupId> <artifactId>groovy-maven-plugin</artifactId> <executions> <execution> <goals> <goal>testCompile</goal> </goals> </execution> </executions></plugin>
Нам также необходимо создать исходную папку, в которой Maven сможет найти исходные файлы Groovy. Путь относительно корневой папки проекта должен быть src / test / groovy. Здесь плагин будет искать исходные файлы по умолчанию. Затем Maven поместит двоичные файлы в target / test-classes.
Чтобы запустить определенный тест с Maven, используйте обычную команду:
mvn test -Dtest = package.TestCase
Некоторые базовые языковые конструкции
Перед созданием первого контрольного примера я должен представить некоторые важные языковые конструкции. Написание Groovy не должно отличаться от написания Java, так как на самом деле можно C & P корректный код Java в класс Groovy. Однако это не даст никаких преимуществ, поэтому давайте посмотрим, что делает Groovy таким привлекательным.
Поскольку Groovy является динамически типизированным языком, нет необходимости указывать тип данных для переменной. Вместо этого можно использовать ключевое слово def.
def person = new Person()
Это может предложить меньше ввода и для узких областей упрощает чтение кода. Остерегайтесь этого при работе с более широкими переменными области видимости, поскольку это делает код менее понятным.
Три основных отличия от Java — обработка строк, списков и карт. Строки, списки (java.util.ArrayList) и карты (java.util.HashMap) встроены в язык, поэтому создавать и работать с ними очень просто.
Сначала кое-что, что будет замечено в примерах кода ниже и может сбить с толку. Это использование точек с запятой. В Groovy точки с запятой не обязательны, и я решил не использовать их.
Строки определены как в Java, но с возможностью использования def. Реальное преимущество заключается в ссылке на другие переменные в строке:
def person = new Person(name:”Tomas Malmsten”)println “The new persons name is $person.name.”
Вышеупомянутое утверждение будет выглядеть в Java:
Person person = new Person(“Tomas Malmsten”);System.out.println(“The new persons name is “ + person.getName() + “.”);
Для создания нового списка все, что вам нужно сделать, это:
def list = [1, 2, 3, 4]
То, что идет между [], создает список, каждый элемент разделяется запятой. Чтобы отсортировать список просто:
sort(list)
Чтобы получить доступ к элементу в списке:
println list[2]//Prints 3
Существует несколько других таких сокращенных команд, все они перечислены в справочном резюме (см. Ссылку в разделе «Дополнительные материалы»),
Карты работают точно так же:
def map = [“one”:1, “two”:2, “three”:3]
Назначение клавиш перед: и значение после.
Чтобы получить доступ к элементам на карте:
println map[“two”]//Prints 2
Есть два способа создать набор:
def set1 = [1, 2, 3, 4] as Setdef set2 = new HashSet()
Второй вид конкретного набора реализации, а не оставляя его на Groovy.
Groovy поддерживает свойства в языке. Это означает, что вместо создания компонента, как в Java:
public class Person { private String name; private int age; private double height; public String getName() {return name;} public void setName(String name) {this.name = name;} ...}
Вы пишете это:
class Person { String name int age double height}
Groovy затем предоставляет два конструктора для всех объектов: конструктор по умолчанию и конструктор, который принимает структуру, подобную карте, с парами имя / значение для заполнения выбранных свойств объектов.
Person person = new Person(name:”Tomas Malmsten”, age:35)
Это создает нового человека с установленными свойствами имени и возраста без необходимости создавать конструктор, который принимает конкретные аргументы.
person.height = 1.82
Это устанавливает свойство высоты. И тот же синтаксис используется для получения свойства:
println person.name
Последняя важная вещь, о которой я здесь расскажу — это операторы возврата метода. Все методы в Groovy возвращают что-то, а именно последний выполненный оператор. Это может быть немного запутанным время от времени, но также весьма полезно. Посмотрите на следующий код:
def createNewPerson(name, age) { new Person(name:name, age:age)}
Оператор return не требуется, поскольку последний выполненный оператор был вызовом конструктора Person. Ключевое слово return все еще может использоваться для возврата значений, особенно если это должно происходить в разделе условной логики или аналогичном.
Создание вашего первого Groovy-теста
Теперь, когда первое введение в Groovy закончено, пришло время посмотреть, как создать базовый модульный тест в Groovy. На самом деле это очень просто, поскольку базовый тестовый класс для Groovy, GroovyTestCase, расширяет TestCase в JUnit. Итак, код, необходимый для создания первого теста с помощью метода теста:
class Test extends GroovyTestCase { void testSomething() { assertTrue(true); }}
Метод доступа для методов тестирования удален, так как область действия по умолчанию для Groovy является общедоступной. Класс будет скомпилирован с использованием компилятора Groovy при вызове цели теста Maven2.
В Groovy используются те же соглашения, что и в JUnit. Для создания setUp или tearDown используйте этот код:
protected void setUp() {}protected void tearDown() {}
Все остальное с тестовым примером Groovy почти такое же, как с тестовым примером JUnit.
Как Groovy может помочь ускорить создание тестов
Недавно я написал тестовый пример для нашей системы сервисов / меню. Система назначает пользователю набор сервисов. У некоторых из сервисов есть пункты меню. Две услуги доступны для клиентов. Одним из них является распознаватель служб, который разрешает службы пользователей в зависимости от ролей, назначенных пользователю. Другой создает меню, используя сервисы. Когда я собираюсь проверить это, мне нужно создать набор сервисов и пунктов меню, которые я затем смогу внедрить в объекты, которые используются для предоставления сервисов. Ниже приведен фрагмент того, как это будет выглядеть в Java:
...group = new MenuGroup();group.setPosition(0);group.setName("TestGroup");topLevelItem1 = new TopLevelItem();topLevelItem1.setPosition(1);topLevelItem1.setParent(group);subLevelItem1 = new SubLevelItem();subLevelItem1.setPosition(2);subLevelItem1.setParent(topLevelItem1);subLevelItem2 = new SubLevelItem();subLevelItem2.setPosition(3);subLevelItem2.setParent(topLevelItem1);Set<Integer> roles = new HashSet<Integer>(3);roles.add(1);roles.add(2);roles.add(3);parent1 = new ParentService();parent1.setMenuItem(topLevelItem1);parent1.setName("parent1");parent1.setRoleIds(roles);parent1.setUrl("parent1");topLevelItem1.setService(parent1);...
Это всего лишь часть реального кода. Теперь давайте посмотрим, как это выглядит в Groovy:
...group = new MenuGroup(position : 0, name : "TestGroup")topLevelItem1 = new TopLevelItem(position : 1, parent : group)subLevelItem1 = new SubLevelItem(position : 2, parent : topLevelItem1)subLevelItem2 = new SubLevelItem(position : 3, parent : topLevelItem1)def roles = [1,2,3] as Setparent1 = new ParentService(menuItem : topLevelItem1,name : "parent1", roleIds : roles, url : "parent1")topLevelItem1.service = parent1...
Таким образом, 22 строки кода Java были сокращены до 8 строк кода Groovy. Это приведет к сокращению времени, необходимого для написания указанного кода. Не говоря уже о том, насколько проще Groovy-код сообщает о намерениях.
Дразнить с EasyMock
Выше приведено довольно простое написание тестов и сбор данных. При работе с созданием кода самопроверки важно также иметь возможность имитировать зависимости. Я всегда использую EasyMock для макетирования зависимостей, поскольку он прост в использовании. Он также поддерживается в тестовых примерах, что создает концептуально чистый дизайн. Поскольку это знакомая мне технология, я решил использовать ее и в Groovy.
С EasyMock легко работать в Groovy, хотя, поскольку статический импорт не поддерживается, он немного сложнее, чем в Java.
Для работы с EasyMock нам нужно добавить зависимость в файл pom.xml, как показано ниже:
<dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>2.2</version> <scope>test</scope></dependency>
Теперь я вернусь к тестам из приведенного выше примера, где я создал тестовый код для системы сервисов / меню, которую мы используем. Поскольку это веб-система, она зависит от интерфейсов HttpServletRequest, HttpServletResponse и HttpSession. Чтобы смоделировать три интерфейса, мне сначала нужно создать три фиктивных элемента управления:
def requestControl = EasyMock.createControl()def responseControl = EasyMock.createControl()def sessionControl = EasyMock.createControl()
Затем я создаю смоделированные реализации, как это:
request = requestControl.createMock(HttpServletRequest.class)session = sessionControl.createMock(HttpSession.class)response = responseControl.createMock(HttpServletResponse.class)
Теперь мне нужно записать, какое поведение ожидается от фиктивных объектов:
EasyMock.expect(request.getSession(false)).andReturn(session)EasyMock.expect(request.getSession(false)).andReturn(session)EasyMock.expect( session.getAttribute(“foo”)).andReturn("")
Далее необходимо воспроизвести макет объекта:
EasyMock.replay(session)EasyMock.replay(request)EasyMock.replay(response)
Теперь я могу использовать объекты для проверки. Когда я проверил их, я также могу проверить, что ожидаемые вызовы были сделаны к интерфейсам, используя операцию verify:
EasyMock.verify(session)EasyMock.verify(request)EasyMock.verify(response)
Чтобы лучше понять, как EasyMock работает на Java и Groovy, есть хорошая документация на сайте EasyMock по адресу http://www.easymock.org. Инструкции легко понять, особенно если вы сначала будете следовать подробным примерам.
дальнейшее чтение
Справочное резюме Groovy — это удобный PDF-документ, который стоит распечатать и иметь при себе при работе с Groovy: http://docs.codehaus.org/download/attachments/2715/groovy-reference-card.pdf
Главная страница Groovy — это место, где живет Groovy и где вы найдете большую часть необходимой документации: http://groovy.codehaus.org/
Сайт EasyMock для всего, что связано с фреймворком: http://www.easymock.org.
PS После написания этой статьи я узнал, что Groovy теперь поддерживает статический импорт. Это отличная новость, и я с нетерпением жду ее использования!