【问题标题】:How do I ignore a test based on another test in NUnit?如何忽略基于 NUnit 中另一个测试的测试?
【发布时间】:2009-11-23 11:15:33
【问题描述】:

我正在为数据库操作编写一些 NUnit 测试。显然,如果Add() 失败,那么Get() 也会失败。但是,当 Add()Get() 都失败时,它看起来很具有欺骗性,因为看起来有两个问题而不是一个。

有没有办法指定运行测试的“顺序”,如果第一个测试失败,则忽略以下测试?

在同一行中,有没有办法自己订购单元测试类?例如,我想先运行基本数据库操作的测试,然后再运行 UI 中的往返数据测试。

注意:这与让测试相互依赖有点不同,它更像是在运行一堆测试之前确保某些东西首先工作。例如,如果您一开始就无法连接到数据库,则运行一堆数据库操作是浪费时间。

编辑:似乎有些人没有抓住重点。我不这样做:

[Test]
public void AddTest()
{
    db.Add(someData);
}

[Test]
public void GetTest()
{
    db.Get(someData);
    Assert.That(data was retrieved successfully);
}

相反,我正在这样做:

[Test]
public void AddTest()
{
    db.Add(someData);
}

[Test]
public void GetTest()
{
    // need some way here to ensure that db.Add() can actually be performed successfully
    db.Add(someData);
    db.Get(somedata);
    Assert.That(data was retrieved successfully);
}

也就是说,我想先确保数据可以添加,然后才能测试是否可以检索。人们假设我使用第一个测试中的数据来通过第二个测试,而事实并非如此。在尝试另一项依赖于它的操作之前,我正在尝试确保一项操作可能

正如我已经说过的,您需要确保在运行数据库操作之前可以连接到数据库。或者您可以在执行文件操作之前打开文件。或者在测试 API 调用之前连接到服务器。或者……你明白了。

【问题讨论】:

  • 接受的答案是错误!查看 cmets

标签: unit-testing nunit


【解决方案1】:

NUnit 支持“Assume.That”语法来验证设置。这被记录为Theory 的一部分(感谢clairestreb)。在NUnit.Framework 命名空间中有一个类Assume。引用文档:

/// Provides static methods to express the assumptions
/// that must be met for a test to give a meaningful
/// result. If an assumption is not met, the test
/// should produce an inconclusive result.

所以在上下文中:

public void TestGet() {
    MyList sut = new MyList()
    Object expecting = new Object();
    sut.Put(expecting);
    Assume.That(sut.size(), Is(1));
    Assert.That(sut.Get(), Is(expecting));
}

【讨论】:

  • 这应该是公认的答案,是声明依赖关系的绝佳方式,而“不确定”正是正确的结果,在大多数测试框架中最终都是橙色的:意思是,仍然需要做一些事情。
  • 7 年后,但我一直在返回这个帖子,所以这里是文档:nunit.org/index.php?p=theory&r=2.5
【解决方案2】:

测试应该从不相互依赖。你才知道为什么。根据定义,相互依赖的测试是脆弱的。如果您需要数据库中的数据来测试Get(),请将其放在设置步骤中。

【讨论】:

  • 除非它不脆弱。你没有抓住重点。在进行测试之前,您需要确保某些东西可用。就我而言,我需要确保 Add() 可以正常工作,因为没有它,Get() 将无法获取任何数据。它是否在设置中并不重要,因为我在我的Get() 测试中使用Add(),但我需要确保Add() 在运行测试之前确实有效。
  • 这就是模拟和存根的用途。在Get() 的测试中,您应该能够为Get() 提供虚拟数据以使用,以便可以在没有Add() 的情况下运行测试。这可能意味着传入Add() 的子类,它不会做任何可能导致它失败的事情。关键是单元测试应该只测试有问题的单元。应该有一种方法可以在不测试 Add() 的情况下测试 Get()。如果您得到了您要求的修复程序,您将无法知道Get() 是否有效,如果Add() 已损坏。这意味着您无法获得系统的可靠快照。
  • @Daniel T. 你没抓住重点。测试应始终相互独立。这意味着在Get() 的测试中使用Add() 不是一个好主意。如果Add()breaks,Get() 的测试也会中断。您的测试不是孤立的。谁说您需要使用Add()Get() 准备数据库...
  • @EricSchaefer:否则您将如何将数据输入数据库?当然,您可以全部模拟和存根,但此时您不再测试数据库。我所要求的正是解决您描述的情况的方法:如果Add() 失败,则不运行Get() 的方法。
  • @EricSchaefer:这(测试应该是独立的)在谈到单元测试时是正确的。 Nunit 也可以用于更高级别的测试,这些测试可能是依赖的。此外,当基本功能失败时,您可能会决定不运行一些复杂的测试,尤其是在试驾开发中。
【解决方案3】:

我认为问题在于您使用 NUnit 运行的不是 NUnit 运行的那种单元测试。

本质上,您希望 AddTest 在 GetTest 之前运行,并且如果 AddTest 失败,您希望 NUnit 停止执行测试。

问题在于这与单元测试是对立的 - 测试应该是完全独立的并且可以按任何顺序运行。

单元测试的标准概念是,如果您围绕“添加”功能进行了测试,那么您可以在“获取”测试中使用“添加”功能,而不必担心“添加”是否在“得到'测试。您知道“添加”有效 - 您可以对其进行测试。

“FIRST”原则 (http://agileinaflash.blogspot.com/2009/02/first.html) 描述了单元测试的行为方式。您要编写的测试违反了“I”(孤立)和“R”(可重复)。

如果您担心两次测试之间的数据库连接断开,我建议您在测试期间不要连接到真实数据库,而应使用某种数据接口,而对于测试,您应该使用模拟接口。如果测试的目的是为了执行数据库连接,那么你可能只是使用了错误的工具来完成这项工作——这并不是真正的单元测试。

【讨论】:

  • 你说的很有道理,但我仍然无法理解如何将Get()Add() 隔离开来。在我看来,这就像一个空盒子。你必须能够在盒子里放东西,然后才能测试是否可以取出。
  • 他们尽可能地孤立。如果你想'Get()',你显然需要在同一个测试(或设置)中首先'Put()'。隔离来自这样一个事实,即您的两个测试正在测试完全不同的东西——您的第一个测试“Put()”,第二个测试“Get()”。如果 'Put()' 失败,它们都会失败 - 但没关系。没有单元测试规则规定如果系统的某个区域发生故障,它应该恰好破坏一个测试 - 它只需要破坏至少一个测试,希望其中一个专门测试该区域。跨度>
  • 我和一位同事谈过,他说虽然在理论上,防止级联失败会很好,但在实践中很少有级联失败实际上阻碍任何事情的情况。换句话说,如果您有 200 多个测试并且测试是否可以从数据库中检索数据的测试失败,那么最好忽略其余测试,因为它们也会自动失败。但是因为测试通常运行很快,并且应该根据您正在测试的层进行分组,所以应该很容易看出问题的根源。
【解决方案4】:

我认为这不可能开箱即用。

无论如何,您所描述的测试类设计将使测试代码非常脆弱。

【讨论】:

  • 我没有运行Add() 测试先将数据添加到数据库中,然后运行Get() 测试以获取数据。相反,我想确保Add()Get() 测试之前首先工作,有点像说“好的,我可以将数据添加到数据库中吗?好,现在添加这个测试数据,看看我是否可以检索它。”
【解决方案5】:

MbUnit 似乎有一个DependsOnAttribute 可以让你做你想做的事。

如果其他测试夹具或测试 方法失败,那么这个测试将不会 跑。此外,依赖力 这个测试在它之后运行 取决于。

但对 NUnit 一无所知。

【讨论】:

【解决方案6】:

您不能假设任何测试夹具执行顺序,因此必须在您的测试类中检查任何先决条件。

将您的 Add 测试分离到一个测试类中,例如AddTests,并将 Get 测试放入另一个测试类,例如类 GetTests。

在 GetTests 类的 [TestFixtureSetUp] 方法中,检查您是否具有有效的数据库访问权限(例如 Add 的工作),如果没有,则根据您认为合适的方式检查 Assert.Ignore 或 Inconclusive。

这将在不满足其先决条件时中止 GetTests 测试夹具,并跳过尝试运行它包含的任何单元测试。 (我想!我是 nUnit 新手。)

【讨论】:

    【解决方案7】:

    创建一个全局变量并在测试中返回Get,除非Add 将其设置为true(在Add 的最后一行执行此操作):

    public boolean addFailed = false;
    public void testAdd () {
        try {
            ... old test code ...
        } catch (Throwable t) { // Catch all errors
            addFailed = true;
            throw t; // Don't forget to rethrow
        }
    }
    public void testGet () {
        if (addFailed) return;
        ... old test code ...
    }
    

    【讨论】:

    • 谢谢。它不是很优雅,但看起来 NUnit 本身并不支持此功能。
    • 你想要的是一个 hack,所以解决方案也是一个 ;)
    • @mlk:这是一个 hack。尽管如此,当您已经知道它会失败并将失败的单一原因埋在一堆继承的错误中时,运行测试是没有意义的。
    • @mlk:如果测试框架有办法说“测试被跳过,因为它无论如何都会失败”,那就太好了。 Python 就是这样做的(例如,当您在 Linux 上运行该套件时进行 Mac/Windows 测试)。
    • 这个解决方案很危险地接近错误,因为它取决于运行的顺序;目前 nunit 根据测试名称运行,所以这很好。但是如果将来 Nunit 改变运行顺序,testGettestAdd 之前首先运行呢?在这种情况下,testAdd 将运行,但 testGet 不会运行!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2017-07-24
    相关资源
    最近更新 更多