Files
spring-webflow/spring-webflow-reference/src/testing.xml
Keith Donald 1967e96e96 polish
2008-10-17 22:00:17 +00:00

146 lines
5.2 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<chapter id="testing">
<title>Testing flows</title>
<sect1 id="testing-introduction">
<title>Introduction</title>
<para>
This chapter shows you how to test flows.
</para>
</sect1>
<sect1 id="extending-abstractflowexecutiontest">
<title>Extending AbstractXmlFlowExecutionTests</title>
<para>
To test the execution of a XML-based flow definition, extend <code>AbstractXmlFlowExecutionTests</code>:
</para>
<programlisting language="java"><![CDATA[
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {
}]]>
</programlisting>
</sect1>
<sect1 id="override-getResource">
<title>Specifying the path to the flow to test</title>
<para>
At a minimum, you must override <code>getResource(FlowDefinitionResourceFactory)</code> to return the path to the flow you wish to test:
</para>
<programlisting language="java"><![CDATA[
@Override
protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) {
return resourceFactory.createFileResource("src/main/webapp/WEB-INF/hotels/booking/booking.xml");
}]]>
</programlisting>
</sect1>
<sect1 id="override-configureFlowBuilderContext">
<title>Registering flow dependencies</title>
<para>
If your flow has dependencies on externally managed services,
also override <code>configureFlowBuilderContext(MockFlowBuilderContext)</code> to register stubs or mocks of those services:
</para>
<programlisting language="java"><![CDATA[
@Override
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
builderContext.registerBean("bookingService", new StubBookingService());
}]]>
</programlisting>
<para>
If your flow extends from another flow, or has states that extend other states,
also override <code>getModelResources(FlowDefinitionResourceFactory)</code> to return the path to the parent flows.
</para>
<programlisting language="java"><![CDATA[
@Override
protected FlowDefinitionResource[] getModelResources(FlowDefinitionResourceFactory resourceFactory) {
return new FlowDefinitionResource[] {
resourceFactory.createFileResource("src/main/webapp/WEB-INF/common/common.xml")
};
}]]>
</programlisting>
</sect1>
<sect1 id="testing-flowstartup">
<title>Testing flow startup</title>
<para>
Have your first test exercise the startup of your flow:
</para>
<programlisting language="java"><![CDATA[
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);
}]]>
</programlisting>
<para>
Assertions generally verify the flow is in the correct state you expect.
</para>
</sect1>
<sect1 id="testing-flowevents">
<title>Testing flow event handling</title>
<para>
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 <code>setCurrentState(String)</code> method to jump to the flow state where you wish to begin your test.
</para>
<programlisting language="java"><![CDATA[
public void testEnterBookingDetails_Proceed() {
setCurrentState("enterBookingDetails");
getFlowScope().put("booking", createTestBooking());
MockExternalContext context = new MockExternalContext();
context.setEventId("proceed");
resumeFlow(context);
assertCurrentStateEquals("reviewBooking");
}]]>
</programlisting>
</sect1>
<sect1 id="testing-mockingsubflows">
<title>Mocking a subflow</title>
<para>
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.
</para>
<programlisting language="java"><![CDATA[
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;
}]]>
</programlisting>
</sect1>
</chapter>