ZOMBIES：如何在软件开发中实现业务需求（四）
测试
![](/data/attachment/album/202305/30/094226xz66t662t42auuht.jpg)



> 
> 完善你的电商应用，使它能够正确处理业务规则。
> 
> 
> 


在前面的文章中，我已经解释了为什么将编程问题看作一整群丧尸来处理是错误的。我用 ZOMBIES 方法来解释为什么循序渐进地处理问题更好。


ZOMBIES 表示以下首字母缩写：


* **Z** – 最简场景（Zero）
* **O** – 单元素场景（One）
* **M** – 多元素场景（Many or more complex）
* **B** – 边界行为（Boundary behaviors）
* **I** – 接口定义（Interface definition）
* **E** – 处理特殊行为（Exercise exceptional behavior）
* **S** – 简单场景用简单的解决方案（Simple scenarios, simple solutions）


在系列的前三篇文章中，我展示了 ZOMBIES 方法的前五项。第一篇中 [实现了最简场景](/article-15808-1.html)，它为代码提供了最简可行路径。第二篇文章中执行了 [单元素场景和多元素场景上的测试](/article-15817-1.html)。第三篇中介绍了 [边界和接口](/article-15859-1.html)。在本文中，我将带你了解倒数第二个方法：处理特殊行为。


### 处理特殊行为


在开发一个电子购物应用时，你需要从产品负责人或赞助商那里了解需要采用什么销售策略。


毫无疑问，与任何电子商业活动一样，你需要通过制定销售策略来诱导顾客进行消费。假设有如下的销售策略：订单金额超过 ￥500 时可以享受一定的折扣优惠。


现在将这个销售策略转换为可运行期望：



```
[Fact]
public void Add2ItemsTotal600GrandTotal540() {
        var expectedGrandTotal = 540.00;
        var actualGrandTotal = 0.00;
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

```

这个正面样例表示的销售策略是，如果订单总额为 ￥600.00，那么 `shoppingAPI` 会将其减价为 ￥540.00。上面的代码伪造了一个失败验证用例。现在修改它，让它能够通过测试：



```
[Fact]
public void Add2ItemsTotal600GrandTotal540() {
        var expectedGrandTotal = 540.00;
        Hashtable item = new Hashtable();
        item.Add("00000001", 200.00);
        shoppingAPI.AddItem(item);
        Hashtable item2 = new Hashtable();
        item2.Add("00000002", 400.00);
        shoppingAPI.AddItem(item2);
        var actualGrandTotal = shoppingAPI.CalculateGrandTotal();
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

```

在这个正样例中，你向购物框加入一件价值 ￥200 的商品和一件价值 ￥400 的商品，使总价达到 ￥600 。当调用 `CalculateGrandTotal()` 方法时，你期望总价是 ￥540。


这个微测试能够通过吗？



```
[xUnit.net 00:00:00.57] tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [FAIL]
  X tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [2ms]
  Error Message:
   Assert.Equal() Failure
Expected: 540
Actual: 600
[...]

```

很可惜，它失败了。你期望的结果是 ￥540，但计算结果为 ￥600。为什么会这样呢？那是因为你还没有告诉系统在订单总价大于 ￥500 时怎么进行折扣计算。


现在来实现折扣计算逻辑。根据上面的正样例可知，当订单总价为 ￥600（超过了营销策略的阈值 ￥500）时，期望的最终总价为 ￥540。也就是说系统需要从订单总额中减去 ￥60。刚好是是原订单总价的 10%。因此该销售规则就是当订单总额超过 ￥500 时享受九折优惠。


在 `ShippingAPI` 类中实现该处理逻辑：



```
private double Calculate10PercentDiscount(double total) {
        double discount = 0.00;
        if(total > 500.00) {
                discount = (total/100) * 10;
        }
        return discount;
}

```

首先，检查订单总额是否大于 ￥500 。如果是，则计算出总额的 10%。


你还需要告诉系统怎么从订单总额中减去 10%。改动非常直接：



```
return grandTotal - Calculate10PercentDiscount(grandTotal);

```

到此，所有测试都能够通过。你又一次享受到系统处于稳态的欢愉。你的代码通过处理特殊行为实现了需要的销售策略。


### 最后一步


现在我已经介绍完 ZOMBIE 了，只剩下 S 了。我将会在最后一篇中介绍它。


*（题图：MJ/7f8bf5d2-54ce-4d6e-9dbf-13abf6df966a）*




---


via: <https://opensource.com/article/21/2/exceptional-behavior>


作者：[Alex Bunardzic](https://opensource.com/users/alex-bunardzic) 选题：[lujun9972](https://github.com/lujun9972) 译者：[toknow-gh](https://github.com/toknow-gh) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
In my previous articles in this series, I explained why tackling coding problems all at once, as if they were hordes of zombies, is a mistake. I'm using a helpful acronym to explain why it's better to approach problems incrementally. **ZOMBIES** stands for:

**Z** – Zero

**O** – One

**M** – Many (or more complex)

**B** – Boundary behaviors

**I** – Interface definition

**E** – Exercise exceptional behavior

**S** – Simple scenarios, simple solutions


In the first three articles in this series, I demonstrated the first five **ZOMBIES** principles. The first article [implemented Zero](https://opensource.com/article/21/1/zombies-zero), which provides the simplest possible path through your code. The second article performed

[tests with](https://opensource.com/article/21/1/zombies-2-one-many)samples, and the third article looked at

**O**ne and**M**any[. In this article, I'll take a look at the penultimate letter in our acronym:](https://opensource.com/article/21/1/zombies-3-boundaries-interface)

**B**oundaries and**I**nterfaces**E**, which stands for "exercise exceptional behavior."

## Exceptional behavior in action

When you write an app like the e-commerce tool in this example, you need to contact product owners or business sponsors to learn if there are any specific business policy rules that need to be implemented.

Sure enough, as with any e-commerce operation, you want to put business policy rules in place to entice customers to keep buying. Suppose a business policy rule has been communicated that any order with a grand total greater than $500 gets a percentage discount.

OK, time to roll up your sleeves and craft the executable expectation for this business policy rule:

```
[Fact]
public void Add2ItemsTotal600GrandTotal540() {
var expectedGrandTotal = 540.00;
var actualGrandTotal = 0.00;
Assert.Equal(expectedGrandTotal, actualGrandTotal);
}
```


The confirmation example that encodes the business policy rule states that if the order total is $600.00, the `shoppingAPI`

will calculate the grand total to discount it to $540.00. The script above fakes the expectation just to see it fail. Now, make it pass:

```
[Fact]
public void Add2ItemsTotal600GrandTotal540() {
var expectedGrandTotal = 540.00;
Hashtable item = new Hashtable();
item.Add("00000001", 200.00);
shoppingAPI.AddItem(item);
Hashtable item2 = new Hashtable();
item2.Add("00000002", 400.00);
shoppingAPI.AddItem(item2);
var actualGrandTotal = shoppingAPI.CalculateGrandTotal();
Assert.Equal(expectedGrandTotal, actualGrandTotal);
}
```


In the confirmation example, you are adding one item priced at $200 and another item priced at $400 for a total of $600 for the order. When you call the `CalculateGrandTotal()`

method, you expect to get a total of $540.

Will this microtest pass?

```
[xUnit.net 00:00:00.57] tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [FAIL]
X tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [2ms]
Error Message:
Assert.Equal() Failure
Expected: 540
Actual: 600
[...]
```


Well, it fails miserably. You were expecting $540, but the system calculates $600. Why the error? It's because you haven't taught the system how to calculate the discount on order totals larger than $500 and then subtract that discount from the grand total.

Implement that processing logic. Judging from the confirmation example above, when the order total is $600.00 (which is greater than the business rule threshold of an order totaling $500), the expected grand total is $540. This means the system needs to subtract $60 from the grand total. And $60 is precisely 10% of $600. So the business policy rule that deals with discounts expects a 10% discount on all order totals greater than $500.

Implement this processing logic in the `ShippingAPI`

class:

```
private double Calculate10PercentDiscount(double total) {
double discount = 0.00;
if(total > 500.00) {
discount = (total/100) * 10;
}
return discount;
}
```


First, check to see if the order total is greater than $500. If it is, then calculate 10% of the order total.

You also need to teach the system how to subtract the calculated 10% from the order grand total. That's a very straightforward change:

`return grandTotal - Calculate10PercentDiscount(grandTotal);`


Now all tests pass, and you're again enjoying steady success. Your script **Exercises exceptional behavior** to implement the required business policy rules.

## One more to go

I've taken us to **ZOMBIE** now, so there's just **S** remaining. I'll cover that in the exciting series finale.

## 2 Comments