代码之家  ›  专栏  ›  技术社区  ›  John Gallagher

单元测试的外部数据文件

  •  5
  • John Gallagher  · 技术社区  · 15 年前

    我是单元测试的新手,我在寻求一些最佳实践建议。我正在用Xcode在Cocoa中编码。

    我有一个方法来验证用户输入的URL。我希望它只接受http://protocol,并且只接受具有有效字符的URL。

    是否可以对此进行一次测试并使用测试数据文件?数据文件提供了示例有效/无效的URL,以及该URL是否应验证。我还使用这个来检查错误消息的描述和域。

    我为什么这么做

    我用JUnit阅读了Java中的实用单元测试,并给出了一个外部数据文件的例子,这让我觉得这是可以的。另外,这意味着我不需要编写很多带有非常相似代码的单元测试来测试不同的数据。

    但另一方面…

    如果我在测试:

    • 无效字符
    • 无效的协议
    • 有效URL

    在同一个测试数据文件中(因此在同一个测试中),这会在以后导致我的问题吗?我读到一次考试只因一个原因而失败。

    我做的还好吗?

    如果有的话,其他人如何在单元测试中使用测试数据?

    3 回复  |  直到 15 年前
        1
  •  5
  •   NamshubWriter    15 年前

    通常,只有在必要时才使用测试数据文件。使用测试数据文件有许多缺点:

    • 测试的代码在测试代码和测试数据文件之间进行拆分。这使得测试更难理解和维护。
    • 您希望尽可能快地进行单元测试。不必要地读取数据文件的测试会降低测试速度。

    在某些情况下,我确实使用数据文件:

    • 输入较大(例如,XML文档)。虽然您可以使用字符串连接来创建一个大的输入,但它会使测试代码难以读取。
    • 测试实际上是测试读取文件的代码。即使在这种情况下,您也可能希望让测试在临时目录中写入一个示例文件,以便测试的所有代码都在一个位置。

    我建议用代码编写测试,而不是对文件中的有效和无效URL进行编码。我建议创建一个无效字符测试、一个无效协议测试、一个无效域测试和一个有效URL测试。如果您认为覆盖率不够,可以创建一个小型集成测试来测试多个有效和无效的URL。这里有一个Java和JUnit的例子:

    public void testManyValidUrls() {
      UrlValidator validator = new UrlValidator();
      assertValidUrl(validator, "http://foo.com");
      assertValidUrl(validator, "http://foo.com/home");
      // more asserts here
    }
    
    private static void assertValidUrl(UrlValidator validator, String url) {
      assertTrue(url + " should be considered valid", validator.isValid(url);
    }
    
        2
  •  1
  •   Mark Seemann    15 年前

    虽然我认为这是一个完全合理的问题,但我认为你不应该过分担心这个问题。严格地说,您正确地认为每个测试应该只测试一件事,但这并不排除您使用数据文件。

    如果您的测试系统(SUT)是一个简单的URL解析器/验证器,我假设它使用一个URL作为参数。因此,可以同时向其中输入无效数据的数量是有限制的。即使您输入的URL同时包含无效字符和无效协议,它也只会导致一个结果(该URL无效)。

    您所描述的是一个数据驱动测试(也称为参数化测试)。如果您保持测试本身的简单性,那么向它提供不同的数据本身就没有问题。

    你什么 需要关注的是,您希望能够快速找到测试失败的原因(如果在几个月后发生这种情况)。如果测试输出指向测试数据文件中的一个特定行,那么您应该能够快速地找出出了什么问题。另一方面,如果您得到的唯一消息是测试失败,并且文件中的任何行可能有错误,那么您将开始看到测试可维护性噩梦的轮廓。

    就个人而言,我稍微倾向于让测试数据尽可能与测试紧密相关。这是因为我认为测试的概念作为可执行规范非常重要。当测试数据在每个测试中进行硬编码时,它可以非常清楚地指定输入和预期输出之间的关系。从测试中删除的数据越多,就越难阅读这个“规范”。

    这意味着我倾向于定义每个测试中输入数据的值。如果我必须编写许多非常类似的测试,其中唯一的变化是输入和/或预期的输出,我将编写一个参数化测试,但仍然从硬编码测试中调用该参数化测试(每个测试都只是一行代码)。我认为我从未使用过外部数据文件。

    但现在,我甚至没有 知道 我的输入是什么,因为我使用 Constrained Non-Determinism . 相反,我使用等价类和 Derived Values .

        3
  •  1
  •   Ray Tayek    15 年前