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

C# C语言中对同一结果进行多个断言的最佳实践#,c#,tdd,nunit,assert,assertions,C#,Tdd,Nunit,Assert,Assertions,你认为对一个结果进行多次断言最干净的方法是什么?在过去,我对它们进行了相同的测试,但现在开始感觉有点脏了,我只是在使用安装程序玩另一个想法 [TestFixture] public class GridControllerTests { protected readonly string RequestedViewId = "A1"; protected GridViewModel Result { get; set;} [TestFixtureSetUp]

你认为对一个结果进行多次断言最干净的方法是什么?在过去,我对它们进行了相同的测试,但现在开始感觉有点脏了,我只是在使用安装程序玩另一个想法

[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));
    }
}

您需要保持的是(然后结束测试)的模式。在您的情况下,所有的安排都在
TestFixtureSetUp
中,测试的动作也是如此。我会重新安排这一点,它可能会变得笨拙,当你有更多的测试。正如Dockers所指出的,应该避免繁重的测试设置,它们可能会成为问题——它们在课堂上的所有测试中都是“一刀切”的,因此可能会比大多数测试需要的更重

如果您想继续进行另一个后续操作,然后进行更多断言,请将其放在单独的测试中

我不反对将多个断言放在同一个测试中,只要它们有助于测试同一事物(即,它们是同一“逻辑断言”的一部分)。在这种情况下,我可以对this.Result.Data的内容进行任意数量的断言-它们都将检查相同的结果值。您的
使用staticsettings获取数据
可以非常清楚地做到这一点。最好在每个断言上使用唯一的失败消息,以便更容易地判断哪个断言失败

或者,您可以将相关的断言封装在一个方法中。如果你不止一次地使用它,这对于通常的干燥原因是有用的,但是我不认为这有什么大的区别

总之
*每个测试执行一个操作
*在操作之后,根据需要使用尽可能多的相关断言来测试一件事情

*在那里结束测试

在同一个测试中有多个断言可能会导致错误,因此这是您应该始终小心的事情

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

在许多情况下,通过将此类逻辑断言显式封装在自定义类型或方法中,您可以获得两全其美的效果。

您可以使用-NUnit加载项在每个测试中运行一个断言:

[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个不同的测试用例,每个断言一个。

我倾向于只在断言本身有价值的情况下,才将它们单独放置。如果我想要一个单独的断言,我会做一个自定义断言:

AssertThatMyObjectMatches(field1, field2, field3, field4, myObject);
但有时,我喜欢在测试中有不止一个例子。我这样做的时候,行为的一半没有价值,没有另一半

Assert.True(list.IsEmpty());

list.Add(new Thing());
Assert.False(list.IsEmpty());
其他人,包括Ruby社区的许多人,对此有不同的看法。这主要是由Dave Astels的博客文章推动的,如下所示:

我发现“每个测试一个断言”方法对于验证之类的事情非常有用,因为每个小方面都是有价值的。否则我就不那么担心了

任何对你和你的团队有效的方法都可能是正确的。当我对什么是正确的事情有了更好的认识后,我倾向于做任何看似简单且容易改变的事情,使之成为正确的事情。我还将大量的单元级
Given/When/Then
注释放在更复杂的示例中,如果类变得太复杂而无法理解,则将其拆分


以这种方式编写测试的原因并不是为了让您能够捕获中断的内容。它是为了帮助人们理解代码并在不破坏任何东西的情况下对其进行更改。

“逻辑断言”是一个很好的词。有趣的是,断言轮盘赌示例本质上是一个“如何通过不执行arrangeact断言而出错”的示例。概念不同吗?@Anthony:概念不同,尽管它们密切相关。如果您遵循AAA(或xUnit测试模式所称的四阶段测试),您就不太可能感受到断言轮盘赌的痛苦,但我仍然希望在同一个断言块中有完全不相关的断言。在NUnit中,通过在断言语句中指定注释,断言轮盘赌的想法可以最小化。不要使用“Assert.That(条件)”,而是使用“Assert.That(条件,failureMessage)”,其中“failureMessage”是有关断言检查内容的信息。这将让您确切地知道单元测试中哪个断言失败。