7

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

因此,他们通过创建一个通用StockExchange接口来解决这个问题,TokyoStockExchange并且DummyStockExchange都实现了基类。因此,获得了依赖倒置原则,并且在PortfolioTest类中可以实例化 a 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 并测试 if 2 + 2 = 4。嗯,是的,显然这是真的。我们仍然可以中断TokyoStockExchange并且测试仍然会成功,因为它正在测试另一个类。如果这一切看起来都是骗人的,那么它也导致不得不编写额外的代码来测试我们知道会起作用的东西。

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

4

2 回答 2

7

这个想法是您希望Portfolio在与TokyoStockExchange. 如果您使用像 Moq 或 Rhino Mocks 这样的模拟框架,那么您可以轻松地模拟不同的输出和行为TokyoStockExchange并编写单元测试以确保Portfolio正确响应。您将为TokyoStockExchange该类编写单独的单元测试。

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

以这样一个简单的类为例,很难理解其价值,但是对于一个更复杂的类,您需要在“实时”类中很难或不可能安排的情况下验证测试用例,单元测试变得更加重要重要的。

于 2012-08-17T02:18:22.490 回答
3

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

单元测试应该是一个白盒测试,您可以在其中单独测试每个代码单元。通常这是指每个类中的公共接口。您模拟它们的依赖关系,以便确保给定一组已知数据,您的单元将返回可预测的结果。

您在单元测试中说“显然一切都会起作用”。这假设您的代码中没有任何错误。如果你能做出这样的假设,那么你一开始就不需要测试任何东西!而且您实际上并不需要对所有内容进行单元测试——如果您只是在您之上调用 API 方法并传递结果Portfolio的薄层,那么您不应该费心对它进行单元测试。StockExchange

另一方面,如果你Portfolio有真正的逻辑,你会想要对它进行单元测试。假设Portfolio有一种方法可以从 中提取数据Stock Exchange,分析数据,并在股票价格出现异常时(例如价格开始快速下跌)向用户发送警报消息。您希望确保警报实际上会在预期条件下触发发送,但您不想坐等下一次股市崩盘。因此,在您的单元测试中,您将制作一个模拟Stock Exchange来生成您想要触发警报的各种值,然后检查它是否真的发生了。如果是这样,很好,如果不是,你只是发现了一个错误。

集成测试将同时测试这两个单元,这也很重要。但是在集成测试中模拟某些类型的场景更加困难,而且对于找出错误实际隐藏的位置也没有多大帮助。如果您对应用程序进行了集成测试,发现它没有在应该发送警报的时候发送警报,那么问题出在哪里?第三方API有bug吗?证券交易所是否向您发送了错误的价值?您的警报系统是否将消息发送到错误的地址?您可能需要一段时间才能发现您的分析方法存在问题。

于 2012-08-17T02:49:44.247 回答