组织单元测试Fixture代码

在前文《为领域对象编写业务一致的单元测试》,我以电子商城为例,介绍了如何为领域对象编写单元测试并保持业务一致性。本文介绍如何组织单元测试Setup步骤里的代码提高代码可读性。 以下是为Order编写的单元测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class OrderStatusTest {

	@Test
	public void should_be_paying_status_when_create_order() {
		// Setup and Exercise
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Paying);
	}

	@Test
	public void should_be_shipping_status_when_pay_order() {
		// Setup
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));

		// Exercise
		order.pay();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Shipping);
	}

	@Test
	public void should_be_deliverying_status_when_ship_order() {
		// Setup
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
		order.pay();

		// Exercise
		order.ship();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Deliverying);
	}

	@Test
	public void should_be_completed_status_when_delivery_order() {
		// Setup
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
		order.pay();
		order.ship();

		// Exercise
		order.delivery();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Completed);
	}
}

每个单元测试的测试过程可以归结如下: 1、测试新订单should_be_paying_status_when_create_order:

2、测试付款should_be_shipping_status_when_pay_order:

3、测试发货should_be_deliverying_status_when_ship_order:

4、测试收货should_be_completed_status_when_delivery_order:

但实际上单元测试的Setup步骤过于冗长且难以理解,例如构建待发货订单的代码无法直观地理解到当前状态为待发货:

1
2
var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
order.pay();

重新组织Setup代码

1、新建OrderFixtures,集中管理所有单元测试需要使用的Order对象,并提供可读性强的方法名:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class OrderFixtures {

	public static Order newOrder() {
		return new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
	}

	public static Order newPayingOrder() {
		return new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
	}

	public static Order newShippingOrder() {
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
		order.pay();
		
		return order;
	}

	public static Order newDeliveryingOrder() {
		var order = new Order(List.of(new OrderItem(Product.of("c1", "iPhone 16"), 3)));
		order.pay();
		order.ship();
		
		return order;
	}
}

2、OrderFixtures将为所有单元测试提供Order对象:

重构单元测试代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class OrderStatusTest {

	@Test
	public void should_be_paying_status_when_create_order() {
		// Setup and Exercise
		var order = OrderFixtures.newOrder();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Paying);
	}

	@Test
	public void should_be_shipping_status_when_pay_order() {
		// Setup
		var order = OrderFixtures.newPayingOrder();

		// Exercise
		order.pay();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Shipping);
	}

	@Test
	public void should_be_deliverying_status_when_ship_order() {
		// Setup
		var order = OrderFixtures.newShippingOrder();

		// Exercise
		order.ship();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Deliverying);
	}

	@Test
	public void should_be_completed_status_when_delivery_order() {
		// Setup
		var order = OrderFixtures.newDeliveryingOrder();

		// Exercise
		order.delivery();
		
		// Verify
		assertThat(order.getStatus()).isEqualTo(OrderStatus.Completed);
	}
}