C#中同一结果的多个断言的最佳实践

时间:2010-01-14 09:40:13

标签: c# tdd nunit assert assertions

您认为对结果执行多个断言的最简洁方法是什么?在过去,我已经对它们进行了相同的测试但是这开始感觉有点脏,我刚刚使用设置玩了另一个想法。

[TestFixture]
public class GridControllerTests
{
    protected readonly string RequestedViewId = "A1";

    protected GridViewModel Result { get; set;}

    [TestFixtureSetUp]
    public void Get_UsingStaticSettings_Assign()
    {
        var dataRepository = new XmlRepository("test.xml");

        var settingsRepository = new StaticViewSettingsRepository();

        var controller = new GridController(dataRepository, settingsRepository);

        this.Result = controller.Get(RequestedViewId);

    }

    [Test]
    public void Get_UsingStaticSettings_NotNull()
    {
        Assert.That(this.Result,Is.Not.Null);
    }

    [Test]
    public void Get_UsingStaticSettings_HasData()
    {
        Assert.That(this.Result.Data,Is.Not.Null);
        Assert.That(this.Result.Data.Count,Is.GreaterThan(0));
    }

    [Test]
    public void Get_UsingStaticSettings_IdMatches()
    {
        Assert.That(this.Result.State.ViewId,Is.EqualTo(RequestedViewId));
    }

    [Test]
    public void Get_UsingStaticSettings_FirstTimePageIsOne()
    {
        Assert.That(this.Result.State.CurrentPage, Is.EqualTo(1));
    }
}

4 个答案:

答案 0 :(得分:15)

在同一个测试中有多个断言可以导致Assertion Roulette,所以你应该小心谨慎。

然而,当断言无关时,断言轮盘主要是一个问题。如果它们在概念上密切相关,那么许多断言通常可以被视为单个逻辑断言

在许多情况下,通过在自定义类型或方法中显式封装此类逻辑断言,您可以充分利用这两个世界。

答案 1 :(得分:3)

您需要遵守的是Arrange, Act, Assert的模式(然后结束测试)。在您的情况下,所有安排都在TestFixtureSetUp中,正在测试的操作也是如此。我会稍微重新安排一下,当你有更多的测试时,它可能会变得笨重。正如Dockers所指出的那样,应该避免繁重的测试设置,它们会成为问题 - 它们在课堂上的所有测试中都“一刀切”,因此可能比大多数测试需要的更重。

如果您想继续进行另一个后续行动,然后再进行更多断言,请将其置于单独的测试中。

在同一个测试中放置多个断言没有问题,只要它们有助于测试相同的东西(即同一个“逻辑断言”的一部分)。在这种情况下,this.Result.Data的内容上的任何数量的断言都可以由我完成 - 它们都将检查相同的结果值。您的Get_UsingStaticSettings_HasData非常清楚地表达了这一点。最好在每个断言上使用唯一的失败消息,以便更容易判断哪个断言失败。

或者,您可以在单个方法中包装相关的断言。如果您不止一次使用它,这对于通常的DRY原因很有用,但除此之外我不认为这是一个很大的区别。

摘要中  *每次测试都要执行一次操作
 *在行动之后,使用尽可能多的相关断言来测试一件事  *在那里结束测试。

答案 2 :(得分:2)

您可以使用Oapt - NUnit Addin为每次测试运行一次断言:

[TestFixture]
public class GridControllerTests
{
  [TestCase, ForEachAssert]
  public void Get_UsingStaticSettings_Assign()
  {
      var dataRepository = new XmlRepository("test.xml");
      var settingsRepository = new StaticViewSettingsRepository();
      var controller = new GridController(dataRepository, settingsRepository);

      var result = controller.Get("A1");

      AssertOne.From(
        () => Assert.That(this.Result,Is.Not.Null),
        () => Assert.That(this.Result.Data,Is.Not.Null),
        () => Assert.That(this.Result.Data.Count,Is.GreaterThan(0)),
        () => Assert.That(this.Result.State.ViewId,Is.EqualTo(RequestedViewId)),
        () => Assert.That(this.Result.State.CurrentPage, Is.EqualTo(1)));
  }
}

这将创建5个不同的测试用例,每个断言一个。

答案 3 :(得分:0)

我倾向于只在他们自己有价值的情况下自己断言。如果我想自己断言,我会做一个自定义断言:

AssertThatMyObjectMatches(field1, field2, field3, field4, myObject);

但有时候,我喜欢在测试中有多个例子。如果有一半的行为没有另一半没有价值,我会这样做。

Assert.True(list.IsEmpty());

list.Add(new Thing());
Assert.False(list.IsEmpty());

其他人,包括Ruby社区的大部分人,都对此有不同的看法。它主要由Dave Astels的博客文章驱动,在这里:

http://www.artima.com/weblogs/viewpost.jsp?thread=35578

我发现“每个测试的一个断言”方法对于诸如验证之类的东西非常有用,其中每个小方面都是有价值的。否则我不会那么担心。

无论对你和你的团队有什么用,都是正确的方法。当我更好地了解正确的事情时,我倾向于做任何看起来简单易行的事情,以后做出正确的事情。我还在更复杂的示例中添加了大量单元级Given / When / Then注释,如果它变得太复杂而无法理解,则会拆分该类。

以这种方式编写测试的原因并非如此,以至于您可以捕获破坏的东西。这是为了帮助人们理解代码并在不破坏事物的情况下对其进行更改。