【问题标题】:Unit-testing with dependencies between tests具有测试之间依赖关系的单元测试
【发布时间】:2010-10-01 21:29:43
【问题描述】:

当你有单元测试时,你如何进行单元测试

  • 一些通用单元测试
  • 更复杂的测试检查边缘情况,取决于一般情况

举个例子,想象一下测试一个 CSV 阅读器(我只是编了一个符号来演示),

def test_readCsv(): ...

@dependsOn(test_readCsv)
def test_readCsv_duplicateColumnName(): ...

@dependsOn(test_readCsv)
def test_readCsv_unicodeColumnName(): ...

我希望子测试只有在其父测试成功时才会运行。这背后的原因是运行这些测试需要时间。许多回到单一原因的失败报告也不会提供信息。当然,我可以将所有边缘情况硬塞到主测试中,但我想知道是否有更结构化的方法来做到这一点。

我发现了这些相关但不同的问题,

更新:

我发现TestNG 对测试依赖项有很好的内置支持。你可以这样写测试,

@Test{dependsOnMethods = ("test_readCsv"))
public void test_readCsv_duplicateColumnName() {
   ...
}

【问题讨论】:

    标签: python unit-testing code-organization


    【解决方案1】:

    就个人而言,我不会担心在单元测试之间创建依赖关系。这对我来说听起来有点代码味道。几点:

    • 如果测试失败,让其他测试失败,并充分了解不利的代码更改造成的问题规模。
    • 测试失败应该是例外而不是常态,那么当绝大多数时间(希望如此!)没有任何好处时,为什么要浪费精力并创建依赖关系呢?如果失败经常发生,那么您的问题不在于单元测试依赖关系,而在于频繁的测试失败。
    • 单元测试应该运行得非常快。如果它们运行缓慢,则将精力集中在提高这些测试的速度上,而不是防止随后的失败。通过更多地解耦代码并使用依赖注入或模拟来做到这一点。

    【讨论】:

    • 是的,但是:如果你有一个失败的东西并导致一连串的失败,你不能保证以正确的顺序得到失败消息来修复它们(尤其是如果你做任何自动- 发现测试)。例如,我们有一组单元测试来检查网络环境是否设置正确,这(除其他外)对新员工来说很方便。如果您丢失了文件 X 并且该文件在 3 个位置进行了符号链接,则您需要先修复丢失的文件。这对新手来说可能并不明显,因此您会降低测试套件的实用性。
    • 在某些情况下,测试之间需要依赖关系。例如:测试登录到外部服务器的 API 包装器。该登录会话需要在测试之间共享。
    • “如果测试失败,让其他测试失败,并了解不利的代码更改造成的问题规模。” ...通过或fail 对于具有未满足依赖关系的测试毫无意义。如果创建对象失败或创建了对象但结果具有错误的属性。您不能对该对象的行为运行任何有意义的测试。有些人可能不同意创造本身是否是一个测试,无论哪种方式,如果它失败,就无法对其进行进一步的测试。因此,有些人主张使用pass、fail、could not test 提供测试报告。尽管支持这一点的框架并不多。
    • 稍后再添加...这种依赖关系对于无法避免大型单体应用的情况非常有用。例如,每个测试的函数都以依赖于其先前状态的方式扩展对象的状态。纯粹主义者会说,“只是模拟不同的状态”,但这可能非常耗时。如果你无法避免这样的巨石,我的经验是简单地构建一次巨石并在每次状态更改发生时进行测试要好得多。请记住,如果测试不能节省您的时间,一开始就不值得这样做。
    【解决方案2】:

    Proboscis 是 TestNG(Java 库)的 python 版本。

    见packages.python.org/proboscis/

    它支持依赖,例如

    @test(depends_on=[test_readCsv])
    public void test_readCsv_duplicateColumnName() {
       ...
    }
    

    【讨论】:

    • 不确定我是否遗漏了什么,但我找不到如何使用 proboscis 同时支持 setup_class() 和 setup()。它似乎有一个@before_class,但没有一个@before。
    【解决方案3】:

    我不确定你指的是什么语言(因为你没有在你的问题中特别提到它)但是对于像 PHPUnit 这样的东西,有一个 @depends 标签,它只会在依赖的情况下运行测试测试已经通过了。

    根据您使用的语言或单元测试,也可能有类似的可用

    【讨论】:

      【解决方案4】:

      我已经为Nose (Python) 实现了一个插件,它增加了对测试依赖项和测试优先级的支持。

      正如在其他答案/cmets 中提到的,这通常是一个坏主意,但是在某些情况下您可能想要这样做(在我的情况下,它是集成测试的性能 - 进入可测试的开销很大状态,分钟与小时)。

      您可以在这里找到它:nosedep。

      一个最小的例子是:

      def test_a:
        pass
      
      @depends(before=test_a)
      def test_b:
        pass
      

      确保test_b 始终在test_a 之前运行。

      【讨论】:

        【解决方案5】:

        您可能想使用pytest-dependency。根据他们的文档,代码看起来很优雅:

        import pytest
        
        @pytest.mark.dependency()
        @pytest.mark.xfail(reason="deliberate fail")
        def test_a():
            assert False
        
        @pytest.mark.dependency()
        def test_b():
            pass
        
        @pytest.mark.dependency(depends=["test_a"])
        def test_c():
            pass
        
        @pytest.mark.dependency(depends=["test_b"])
        def test_d():
            pass
        
        @pytest.mark.dependency(depends=["test_b", "test_c"])
        def test_e():
            pass
        

        请注意,它是pytest 的插件,而不是unittest,它是python 本身的一部分。因此,您还需要 2 个依赖项(例如添加到 requirements.txt):

        pytest==5.1.1
        pytest-dependency==0.4.0
        

        【讨论】:

          【解决方案6】:

          根据最佳实践和单元测试原则,单元测试不应依赖于其他测试。

          每个测试用例都应该检查具体的孤立行为。

          如果某些测试用例失败了,您将确切知道我们的代码出了什么问题。

          【讨论】:

          • 我和 OP 有同样的问题。我有一个测试构造函数的测试。另一个测试类上的方法的测试。显然,如果构造函数失败,两个测试都会失败,这隐藏了我的代码有什么问题。构造函数测试应该失败,方法测试应该跳过。
          • 这没有抓住问题的重点。第二个测试可以独立于第一个独立运行,并检查具体的、孤立的行为。但是,如果第一个测试失败,那么第二个测试肯定会失败,因此运行它没有任何意义。
          猜你喜欢
          • 2019-05-14
          • 1970-01-01
          • 1970-01-01
          • 2018-01-24
          • 1970-01-01
          • 2014-06-19
          • 2017-11-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多