testing.md 3.7 KB
Newer Older
dallascao's avatar
dallascao 已提交
1 2
# 14. 测试流程

茶陵後's avatar
茶陵後 已提交
3
## 14.1.导言
dallascao's avatar
dallascao 已提交
4 5 6

本章向你展示了如何测试流。

茶陵後's avatar
茶陵後 已提交
7
## 14.2.扩展 AbstractXMLFlowExecutionTests 
dallascao's avatar
dallascao 已提交
8 9 10 11 12 13 14 15 16 17

要测试基于 XML 的流定义的执行情况,请扩展`AbstractXmlFlowExecutionTests`:

```
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {

}
		
```

茶陵後's avatar
茶陵後 已提交
18
## 14.3.指定要测试的流的路径
dallascao's avatar
dallascao 已提交
19 20 21 22 23 24 25 26 27 28 29

至少,你必须重写`getResource(FlowDefinitionResourceFactory)`才能返回你希望测试的流的路径:

```
@Override
protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) {
	return resourceFactory.createFileResource("src/main/webapp/WEB-INF/hotels/booking/booking.xml");
}
		
```

茶陵後's avatar
茶陵後 已提交
30
## 14.4.注册流依赖项
dallascao's avatar
dallascao 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

如果你的流依赖于外部管理的服务,也可以覆盖`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")
};
}
		
```

茶陵後's avatar
茶陵後 已提交
54
## 14.5.测试流启动
dallascao's avatar
dallascao 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

让你的第一个测试练习启动你的流:

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

断言通常会验证流处于你期望的正确状态。

茶陵後's avatar
茶陵後 已提交
77
## 14.6.测试流事件处理
dallascao's avatar
dallascao 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

定义额外的测试来执行流事件处理行为。你的目标应该是在流程中锻炼所有的路径。你可以使用方便的`setCurrentState(String)`方法跳转到你希望开始测试的流状态。

```
public void testEnterBookingDetails_Proceed() {

	setCurrentState("enterBookingDetails");

	getFlowScope().put("booking", createTestBooking());

	MockExternalContext context = new MockExternalContext();
	context.setEventId("proceed");
	resumeFlow(context);

	assertCurrentStateEquals("reviewBooking");
}
		
```

茶陵後's avatar
茶陵後 已提交
97
## 14.7.模拟一个子流
dallascao's avatar
dallascao 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

要测试调用一个子流,请注册一个子流的模拟实现,该实现断言输入被正确地传入,并为你的测试场景返回正确的结果。

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