# 14. 测试流程
# 14.1.导言
本章向你展示了如何测试流。
# 14.2.扩展 AbstractXMLFlowExecutionTests
要测试基于 XML 的流定义的执行情况,请扩展AbstractXmlFlowExecutionTests
:
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {
}
# 14.3.指定要测试的流的路径
至少,你必须重写getResource(FlowDefinitionResourceFactory)
才能返回你希望测试的流的路径:
@Override
protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) {
return resourceFactory.createFileResource("src/main/webapp/WEB-INF/hotels/booking/booking.xml");
}
# 14.4.注册流依赖项
如果你的流依赖于外部管理的服务,也可以覆盖configureFlowBuilderContext(MockFlowBuilderContext)
以注册这些服务的存根或模拟:
@Override
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
builderContext.registerBean("bookingService", new StubBookingService());
}
如果你的流从另一个流扩展,或者具有扩展其他状态的状态,那么也覆盖getModelResources(FlowDefinitionResourceFactory)
以返回父流的路径。
@Override
protected FlowDefinitionResource[] getModelResources(FlowDefinitionResourceFactory resourceFactory) {
return new FlowDefinitionResource[] {
resourceFactory.createFileResource("src/main/webapp/WEB-INF/common/common.xml")
};
}
# 14.5.测试流启动
让你的第一个测试练习启动你的流:
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);
}
断言通常会验证流处于你期望的正确状态。
# 14.6.测试流事件处理
定义额外的测试来执行流事件处理行为。你的目标应该是在流程中锻炼所有的路径。你可以使用方便的setCurrentState(String)
方法跳转到你希望开始测试的流状态。
public void testEnterBookingDetails_Proceed() {
setCurrentState("enterBookingDetails");
getFlowScope().put("booking", createTestBooking());
MockExternalContext context = new MockExternalContext();
context.setEventId("proceed");
resumeFlow(context);
assertCurrentStateEquals("reviewBooking");
}
# 14.7.模拟一个子流
要测试调用一个子流,请注册一个子流的模拟实现,该实现断言输入被正确地传入,并为你的测试场景返回正确的结果。
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;
}