11

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

[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

4 回答 4

15

在同一个测试中拥有多个断言可能会导致Assertion Roulette,因此您应该始终小心这一点。

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

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

于 2010-01-14T09:47:33.070 回答
3

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

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

我对在同一个测试中放置多个断言没有问题,只要它们有助于测试相同的东西(即是同一个“逻辑断言”的一部分)。在这种情况下,我可以对 this.Result.Data 的内容进行任何数量的断言——它们都会检查相同的结果值。你Get_UsingStaticSettings_HasData做得很清楚。最好在每个断言上使用唯一的失败消息,以便更容易判断哪个断言失败。

或者,您可以将相关的断言包装在一个方法中。如果您多次使用它,这对于通常的 DRY 原因很有用,但除此之外我看不出它有很大的不同。

总结
* 每个测试做一个动作
* 在动作之后,使用尽可能多的相关断言来测试一件事
* 在那里结束测试。

于 2010-01-14T09:43:19.083 回答
2

您可以使用Oapt - 一个 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 个不同的测试用例,每个断言一个。

于 2010-05-29T13:00:36.317 回答
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注释,如果它变得太复杂而无法理解,则将其拆分。

以这种方式编写测试的原因不是为了让您可以捕捉到破坏的东西。这是为了帮助人们理解代码并在不破坏代码的情况下对其进行更改。

于 2010-06-23T19:29:39.533 回答