To test the execution of a XML-based flow definition, extend AbstractXmlFlowExecutionTests
:
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests { }
At a minimum, you must override getResource(FlowDefinitionResourceFactory)
to return the path to the flow you wish to test:
@Override protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) { return resourceFactory.createFileResource("src/main/webapp/WEB-INF/hotels/booking/booking.xml"); }
If your flow has dependencies on externally managed services,
also override configureFlowBuilderContext(MockFlowBuilderContext)
to register stubs or mocks of those services:
@Override protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) { builderContext.registerBean("bookingService", new StubBookingService()); }
If your flow extends from another flow, or has states that extend other states,
also override getModelResources(FlowDefinitionResourceFactory)
to return the path to the parent flows.
@Override protected FlowDefinitionResource[] getModelResources(FlowDefinitionResourceFactory resourceFactory) { return new FlowDefinitionResource[] { resourceFactory.createFileResource("src/main/webapp/WEB-INF/common/common.xml") }; }
Have your first test exercise the startup of your flow:
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); }
Assertions generally verify the flow is in the correct state you expect.
Define additional tests to exercise flow event handling behavior.
You goal should be to exercise all paths through the flow.
You can use the convenient setCurrentState(String)
method to jump to the flow state where you wish to begin your test.
public void testEnterBookingDetails_Proceed() { setCurrentState("enterBookingDetails"); getFlowScope().put("booking", createTestBooking()); MockExternalContext context = new MockExternalContext(); context.setEventId("proceed"); resumeFlow(context); assertCurrentStateEquals("reviewBooking"); }
To test calling a subflow, register a mock implementation of the subflow that asserts input was passed in correctly and returns the correct outcome for your test scenario.
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; }