在一个测试中使用多个断言被认为是很有帮助的
TDD 的一个信条是每个测试应该只有一个“断言”。 这是一个金融/交易的例子,在这种情况下,它不能很好地工作。
引言
TDD 的一个信条是每个测试应该只有一个“断言”。 这只是一个指导原则,并且作为一般规则,这是一个非常好的原则。 然而,很多时候,我发现自己想要对一组数据进行断言。
因此,如果你以某种方式看待它,你可以说我想要一个单一的断言,但是针对一组数据,而不仅仅是该数据的单个组件。 另一种看待它的方式是,我实际上是在一个测试中测试了太多的东西。 我认为下面的方法读起来非常好,并且比手动执行多个 Assert.AreEqual
调用或尝试创建一堆测试来测试数据集的每个特定部分,更能传达测试的意图。
背景
你应该熟悉单元测试框架和 TDD,才能充分利用本文。 这里的例子是用 C# 编写的,但我认为一般的想法适用于任何语言/测试框架。
Using the Code
假设我们想要测试一组数据,并且在我们的测试中,我们正在检查这个
Assert.AreEqual(75, allocations["FUND1"].Quantity);
Assert.AreEqual(15, allocations["FUND2"].Quantity);
Assert.AreEqual(10, allocations["FUND3"].Quantity);
由于我还没有提供我正在这样做的上下文,我不认为你现在可以确定你是否认为在单个测试中使用它是一个好主意。 所以现在,请耐心等待,看看下面的内容,它做了同样的事情(而且更多,因为你不必在访问上面的示例中的 Quantity
属性之前检查 null
值)。
ExpectFundAllocations(allocations, @"
FUND1 75
FUND2 15
FUND3 10
");
这是辅助方法 ExpectFundAllocations
的代码
public void ExpectFundAllocations(Allocations allocs, string matchLinesOneString)
{
//get values - word, whitespace, possibly negative number
Regex regex = new Regex(@"^(\w+)\s+(-?\d+)$");
string[] matchLines = Regex.Split(Environment.NewLine, matchLinesOneString);
foreach (string inputRaw in matchLines)
{
//trim, skip blank lines
string input = inputRaw.Trim();
if (String.IsNullOrEmpty(input)) continue;
Match match = regex.Match(input);
if (!match.Success) throw new Exception("Unable to match " + input);
string FundName = match.Groups[1].Value;
decimal Quantity = Convert.ToDecimal(match.Groups[2].Value);
Allocation alloc = allocs[FundName];
Assert.IsNotNull(alloc, "No allocation for " + FundName);
Assert.AreEqual(FundName, alloc.Fund.Name);
Assert.AreEqual(Quantity, alloc.Quantity);
}
}
完整的测试
[Test]
public void TradeAllocatesEvenlyToMultipleFunds()
{
Trade trade = new Trade
{
Investment = new Investment { Symbol = "IBM" },
Type = TradeType.Buy,
FilledQuantity = 100
};
Funds fundsToAllocateTo = new Funds {
new Fund { Name = "FUND1", RelativeWeighting = .75M },
new Fund { Name = "FUND2", RelativeWeighting = .15M },
new Fund { Name = "FUND3", RelativeWeighting = .10M },
};
FundAllocationCalculator calculator =
new FundAllocationCalculator(fundsToAllocateTo);
Allocations allocations = calculator.CreateAllocationsForTrade(trade);
ExpectFundAllocations(allocations, @"
FUND1 75
FUND2 15
FUND3 10
");
}
业务背景
我不喜欢对抽象概念进行争论,所以我为本文使用了一个非常具体的业务示例,我在其中使用了这种方法。 假设你在一家从事交易的金融公司工作。 在这个特定的例子中,我们正在购买 100 股 IBM 股票。 你碰巧工作的这个地方为多个基金执行这些交易,其中一些基金比其他基金大。 因此,如果你有三个基金,你不会执行三笔交易,而是会执行一笔交易,然后根据各种标准将这些股票分配给这些基金。 在这里,我简化了这些标准,使其只是一个百分比,并且所有基金的百分比加起来为 100%。
以上测试的想法是,我们有每个基金的比率,当我们分配时,我们希望所有这些都与该基金的比率相对应。
在这个特定的例子中,你可以争辩说我应该只测试一个基金乘以比率等于该基金的股票数量。 但这将是一个简单的乘法测试,并不能真正传达我试图测试的业务场景。 此外,还有诸如四舍五入之类的其他复杂情况,并确保分配了所有股票,我在这里没有显示,这将使对单个基金的测试不是很有用。
对于另一个需要查看集合的业务示例,可能会有另一个测试,其中我们正在处置股票而不是获取股票,并且其中一个基金在该特定交易中没有足够的股票以其正常的基金百分比参与。 由于股票已经交易,因此其他基金将获得额外的股票。
关注点
在上面的示例中,我演示了使用正则表达式来测试集合。 相同的概念可以用于构建基金分配的测试部分。 对于多个测试,它将更具可读性并且更容易指定。 同样,如果我们要涉及交易的持仓,我们可以使用相同的方法来指定,再次使其更具可读性。
历史
- 初始版本 - 2009 年 3 月 1 日