对为什么它对单元测试虚拟对象有用感到困惑

时间:2012-08-17 02:13:42

标签: c# unit-testing testing tdd

我正在阅读Clean Code: A Handbook of Agile Software Craftsmanship,其中一个示例涉及Portfolio班级和TokyoStockExchange班级。但是,Portfolio不是非常可测试的,因为它依赖TokyoStockExchange作为外部API来确定投资组合的价值,这是一个非常不稳定的查找,不利于测试。

因此,他们通过创建一个公共StockExchange接口来解决这个问题,并让TokyoStockExchangeDummyStockExchange都实现基类。因此,实现了依赖性倒置原则,并且在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,测试仍然会成功,因为它正在测试另一个类。如果这一切看起来都具有欺骗性,它还会导致必须编写额外的代码来测试我们知道的东西。

我认为这是我在理解单元测试时遇到的最大问题。我知道我错了,我只是没有看到我猜的光。希望有人可以帮助我。

2 个答案:

答案 0 :(得分:7)

我们的想法是,您希望独立于Portfolio来测试TokyoStockExchange类中的逻辑。如果您使用像Moq或Rhino Mocks这样的模拟框架,那么您可以轻松地模拟来自TokyoStockExchange的不同输出和行为,并编写单元测试以确保Portfolio正确响应。您可以为TokyoStockExchange类编写单独的单元测试。

这并不是说您不需要在两个类之间进行集成测试。如果不使用模拟对象,很难正确验证所有场景。

使用这样一个简单的类作为一个例子很难理解这个值,但考虑到一个更复杂的类,你需要验证测试用例,以便在“实时”类中很难或不可能安排,单元测试变得更加重要。

答案 1 :(得分:3)

您应该进行两种测试,单元测试和集成测试。

单元测试应该是一个白盒测试,您可以单独测试每个代码单元。通常,这指的是每个类中的公共接口。你嘲笑他们的依赖关系,以便你可以放心,如果给定一组已知数据,你的单位将返回可预测的结果。

你说,“显然一切都会在单元测试中发挥作用”。这预先假定您的代码中没有任何错误。如果您可以做出这样的假设,那么您首先不需要测试任何东西!并且您并不需要对所有内容进行单元测试 - 如果您的Portfolio只是StockExchange上的一个薄层,它调用API方法并传递结果,那么您不应该费心去做单元测试它。

另一方面,如果你的Portfolio中有真实的逻辑,你可能需要对它进行单元测试。假设Portfolio有一种方法可以从Stock Exchange中提取数据,分析数据,并在股票价格出现异常情况时向用户发送提醒信息,例如价格是否开始迅速下降。您需要确保在预期条件下实际发送警报,但您不想坐下来等待下一次股市崩盘。因此,在您的单元测试中,您将创建一个模拟Stock Exchange,它会生成您想要触发警报的各种值,然后检查它是否实际发生。如果它确实如此,那么,如果没有,你就会发现一个错误。

集成测试将串联测试两个单元,也很重要。但是在集成测试中模拟某些场景更加困难,而且在确定bug实际隐藏的位置时没那么有用。如果您为应用程序运行了集成测试,并发现它应该在应该发送警报时,问题出在哪里?第三方API中是否存在错误?股票交易所会给你带来坏的价值吗?您的警报系统是否将消息发送到错误的地址?您可能需要一段时间才能发现分析方法存在问题。