# 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; } ```