我正在阅读Clean Code: A Handbook of Agile Software Craftsmanship
,其中一个示例涉及Portfolio
类和TokyoStockExchange
类。但是,Portfolio
不是很可测试,因为它依赖于TokyoStockExchange
作为外部 API 来确定投资组合的价值,这是一个非常不稳定的查找,不利于测试。
因此,他们通过创建一个通用的StockExchange
接口来解决这个问题,并让TokyoStockExchange
和DummyStockExchange
都实现基类。因此,实现了依赖反转原则,在PortfolioTest
类中,人们可以实例化一个DummyStockExchange
,为公司固定股票价格,将DummyStockExchange
实例分配给投资组合,并将该公司的一些股票添加到投资组合中,然后断言期望值是否确实是适当的价值。代码如下:
public class PortfolioTest
{
private DummyStockExchange exchange;
private Portfolio portfolio;
protected void setUp()
{
exchange = new DummyStockExchange();
exchange.fix("MSFT", 100);
portfolio = new Portfolio(exchange);
}
public void GivenFiveMSFTTotalShouldBe500()
{
portfolio.add(5, "MSFT");
Assert.assertEquals(500, portfolio.value());
}
}
我的问题是,很简单,为什么?
我们试图测试TokyoStockExchange
类是否与Portfolio
类协同工作。 显然,如果我们使用新方法创建另一个类来设定股票价格,然后给投资组合提供其中的五只股票,那么一切都会起作用。只是看起来..测试没用。我知道由于股票价格的变化,TokyoStockExchange
基本上不可能用Portfolio
进行测试,但我不明白在一个相当无用的测试中订阅对这种情况有什么帮助。
这一切似乎类似于不知道我们的加法器程序是否有效,但唯一可用的数字是随机生成的,因此我们创建了一个虚拟类,给我们一个 2 并测试是否2 + 2 = 4
。嗯,是的,显然这是真的。我们仍然可以打破TokyoStockExchange
测试仍然会成功,因为它正在测试另一个类。如果有的话,这一切似乎都是欺骗性的,它还导致不得不编写额外的代码来测试我们知道会起作用的东西。
我认为这是我在理解单元测试时遇到的最大问题。我知道我错了,我只是没有看到我猜想的光。希望有人可以帮助我。
这个想法是,您可能希望独立于TokyoStockExchange
测试Portfolio
类中的逻辑。如果您使用像 Moq 或 Rhino Mocks 这样的模拟框架,那么您可以轻松地模拟来自TokyoStockExchange
的不同输出和行为,并编写单元测试以确保Portfolio
正确响应。您将为 TokyoStockExchange
类编写单独的单元测试。
这并不是说你不需要在两个类之间进行集成测试。只是很难在不使用模拟对象的情况下正确验证所有方案。
以这样一个简单的类为例很难理解其价值,但是给定一个更复杂的类,您需要验证测试用例,以应对难以或不可能在"实时"类上安排的情况,单元测试变得更加重要。
您应该执行两种类型的测试,单元测试和集成测试。
单元测试应该是一个白盒测试,您可以在其中隔离测试每个代码单元。通常,这是指每个类中的公共接口。您模拟出它们的依赖关系,以便您确信给定一组已知的数据,您的单位将返回可预测的结果。
你在单元测试中说,"显然一切都会起作用"。前提是您的代码中没有任何错误。如果您可以做出这样的假设,那么您首先就不需要测试任何东西!而且你真的不需要对所有东西进行单元测试---如果你的Portfolio
只是StockExchange
上的一层薄薄的,它调用 API 方法并传递结果,你不应该费心对它进行单元测试。
另一方面,如果你的Portfolio
有真正的逻辑,你会想要对它进行单元测试。假设Portfolio
有一种方法可以从Stock Exchange
中提取数据,分析数据,并在股票价格显示某些异常时向用户发送警报消息,例如价格开始快速下跌。您希望确保在预期条件下确实会触发警报,但您不想坐等下一次股市崩盘。因此,在单元测试中,您将创建一个模拟Stock Exchange
,以生成要触发警报的值类型,然后检查它是否确实发生。如果是这样,那就太好了,如果没有,你刚刚发现了一个错误。
集成测试将同时测试这两个单元,这一点也很重要。但是,在集成测试中模拟某些类型的场景更加困难,并且在确定错误实际隐藏的位置方面也不太有帮助。如果对应用程序运行集成测试,发现它没有在应该发送警报时发送警报,那么问题出在哪里?第三方 API 中是否存在错误?证券交易所是否向您发送了不良价值?您的警报系统是否将消息发送到错误的地址?您可能需要一段时间才能确定分析方法存在问题。