Если вы пишете под Liferay, используете Service Builder и не знаете как протестировать свою логику, тогда этот пост для вас.
В сети можно найти различные варианты решения данной задачи. Многие из них направлены на специфическую конфигурацию зависимостей, которые позволят сервисам работать с реальной базой данных. Такие подходы сложны и противоречат принципам юнит тестирования. В этом случае надо либо поднимать базу в памяти и настраивать на ее использования сервиса, либо создавать заглушки, они же «моки», самих сервисов. Поэтому бизнес логику лучше вынести в отдельный слой.
Пока нет ничего сложного. С подходом определились, теперь необходимо найти инструмент, который позволит создавать заглушки на любой сгенерированный сервис. Такой инструмент есть — Mockito. В качестве льтернативы могу предложить Easymock, но примеры будут на Mockito.
Для примера возьмем сущность Status и опишем ее в service.xml:
Соберем сервиса, после чего получим ряд классов, который все вместе образуют DAO слой для этой сущности. Обычно в бизнес логике используются классы *LocalServiceUtil, в нашем случае это класс StatusLocalServiceUtil. Допустим что у нас есть некий класс Controller, который вызывает метод getStatus класса StatusLocalServiceUtil и выполняет некую логику с результатом:
Целью является создание заглушки, которая будет эмулировать работу метода getStatus. А сложность в том, что этот метод статический и подменить его сложно, но возможно. Далее следует пример юнит теста, в котором происходит создание заглушки:
На самом деле дела обстоят сложнее и требуют более глубоких объяснений иерархии генерируемых сервисов, но для тестирования этого достаточно.
В сети можно найти различные варианты решения данной задачи. Многие из них направлены на специфическую конфигурацию зависимостей, которые позволят сервисам работать с реальной базой данных. Такие подходы сложны и противоречат принципам юнит тестирования. В этом случае надо либо поднимать базу в памяти и настраивать на ее использования сервиса, либо создавать заглушки, они же «моки», самих сервисов. Поэтому бизнес логику лучше вынести в отдельный слой.
Пока нет ничего сложного. С подходом определились, теперь необходимо найти инструмент, который позволит создавать заглушки на любой сгенерированный сервис. Такой инструмент есть — Mockito. В качестве льтернативы могу предложить Easymock, но примеры будут на Mockito.
Для примера возьмем сущность Status и опишем ее в service.xml:
<entity name="Status" table="status" local-service="true" remote-service="false">
<column name="id" db-name="status_id" type="long" primary="true" id-type="increment"/>
<column name="name" db-name="name" type="String"/>
</entity>
Соберем сервиса, после чего получим ряд классов, который все вместе образуют DAO слой для этой сущности. Обычно в бизнес логике используются классы *LocalServiceUtil, в нашем случае это класс StatusLocalServiceUtil. Допустим что у нас есть некий класс Controller, который вызывает метод getStatus класса StatusLocalServiceUtil и выполняет некую логику с результатом:
public class Controller {
public void doAction(long statusId) throws SystemException, PortalException {
// Получаем статус по его id
Status status = StatusLocalServiceUtil.getStatus(statusId);
// и выводим его в консоль
System.out.println("Status = " + status);
}
}
Целью является создание заглушки, которая будет эмулировать работу метода getStatus. А сложность в том, что этот метод статический и подменить его сложно, но возможно. Далее следует пример юнит теста, в котором происходит создание заглушки:
public class ControllerTest {
private StatusLocalService service;
private Controller controller;
@BeforeMethod
public void beforeMethod() {
// Создаем мок сервиса используя библиотеку mockito
service = mock(StatusLocalService.class);
// После этой строчки класс StatusLocalServiceUtil
// будет использовать зуглушку или мок сервиса
new StatusLocalServiceUtil().setService(service);
// А это контроллер, который работает со
// сгенерированными сервисами и ничего не знает
// ни о каких моках
controller = new Controller();
}
@Test
public void testGetStatus()
throws ServletException, SystemException, PortalException {
// Конфигурируем сервис таким образом, что при вызове
// метода с параметром 7 вернется уже другой мок —
// мок объекта Status
when(service.getStatus(eq(7L))).thenReturn(mock(Status.class));
// Вот он самый, вызов метода, который тестируется
controller.doAction(7);
// А здесь проверяем был ли вызов метода getStatus
// именно с параметром 7
verify(service).getStatus(eq(7L));
}
}
На самом деле дела обстоят сложнее и требуют более глубоких объяснений иерархии генерируемых сервисов, но для тестирования этого достаточно.