Spring Web Flow — тестирование потоков (Testing flows)
15. Spring Web Flow — тестирование потоков (Testing flows)
15.1. Введение
Эта глава рассказывает о тестировании потоков.
Для выполнения тестов при определении потоков с помощью XML настроек необходимо расширить класс AbstractXmlFlowExecutionTests:
1 |
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests { } |
Как минимум, вы должны переопределить метод getResource(FlowDefinitionResourceFactory) для возвращения пути к потоку, который вы хотите протестировать:
1 2 3 4 |
@Override protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) { return resourceFactory.createFileResource("src/main/webapp/WEB-INF/hotels/booking/booking.xml"); } |
Если ваш поток имеет внешние службы (сервисы), то так же переопределите метод configureFlowBuilderContext(MockFlowBuilderContext) для регистрации mock этих внешних служб:
1 2 3 4 |
@Override protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) { builderContext.registerBean("bookingService", new StubBookingService()); } |
Если ваш поток расширяет другой поток или его состояние расширяет другое состояние, то необходимо переопределить метод getModelResources(FlowDefinitionResourceFactory) на возвращение пути родительского потока.
1 2 3 4 5 6 |
@Override protected FlowDefinitionResource[] getModelResources(FlowDefinitionResourceFactory resourceFactory) { return new FlowDefinitionResource[] { resourceFactory.createFileResource("src/main/webapp/WEB-INF/common/common.xml") }; } |
Так можно протестировать запуск потока:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public void testStartBookingFlow() { Booking booking = createTestBooking(); MutableAttributeMap input = new LocalAttributeMap(); input.put("hotelId", "1"); MockExternalContext context = new MockExternalContext(); context.setCurrentUser("keith"); startFlow(input, context); assertCurrentStateEquals("enterBookingDetails"); assertTrue(getRequiredFlowAttribute("booking") instanceof Booking); } |
Здесь сверяется достижение потока ожидаемого состояния.
Вашей целью должно быть достижение всех заданных путей в потоке. Вы можете использовать метод setCurrentState(String) для прыжка к состоянию потока, где должен начаться тест.
1 2 3 4 5 6 7 8 9 10 11 12 |
public void testEnterBookingDetails_Proceed() { setCurrentState("enterBookingDetails"); getFlowScope().put("booking", createTestBooking()); MockExternalContext context = new MockExternalContext(); context.setEventId("proceed"); resumeFlow(context); assertCurrentStateEquals("reviewBooking"); } |
Для теста вызова подпотока зарегистрируйте реализацию mock-объекта подпотка, который сверяет введенные параметры и возвращаемые в вашем тестовом сценарии.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public void testBookHotel() { setCurrentState("reviewHotel"); Hotel hotel = new Hotel(); hotel.setId(1L); hotel.setName("Jameson Inn"); getFlowScope().put("hotel", hotel); getFlowDefinitionRegistry().registerFlowDefinition(createMockBookingSubflow()); MockExternalContext context = new MockExternalContext(); context.setEventId("book"); resumeFlow(context); // verify flow ends on 'bookingConfirmed' assertFlowExecutionEnded(); assertFlowExecutionOutcomeEquals("finish"); } public Flow createMockBookingSubflow() { Flow mockBookingFlow = new Flow("booking"); mockBookingFlow.setInputMapper(new Mapper() { public MappingResults map(Object source, Object target) { // assert that 1L was passed in as input assertEquals(1L, ((AttributeMap) source).get("hotelId")); return null; } }); // immediately return the bookingConfirmed outcome so the caller can respond new EndState(mockBookingFlow, "bookingConfirmed"); return mockBookingFlow; } |