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

发布于 2024-08-17 19:17:33 字数 1217 浏览 7 评论 0原文

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

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

What do you think is cleanest way of doing multiple asserts on a result? In the past I've put them all the same test but this is starting to feel a little dirty, I've just been playing with another idea using 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));
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

愁杀 2024-08-24 19:17:33

在同一个测试中使用多个断言可能会导致断言轮盘,所以这是你应该做的事情一定要小心。

然而,当断言不相关时,断言轮盘赌通常会出现问题。如果它们在概念上密切相关,则许多断言通常可以被视为单个逻辑断言。

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

Having multiple assertions in the same test can lead to Assertion Roulette, so this is something of which you should always be careful.

However, Assertion Roulette is mostly a problem when the assertions are unrelated. If they are conceptually closely related, many assertions can often be viewed as a single Logical Assertions.

In many cases you can get the best of both worlds by explicitly encapsulating such a Logical Assertion in a a custom type or method.

樱花细雨 2024-08-24 19:17:33

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

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

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

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

总结
* 每次测试执行一项操作
* 操作之后,根据需要使用尽可能多的相关断言来测试一件事
* 在那里结束测试。

What you need to keep to is the pattern of Arrange, Act, Assert (and then end the test). In your case all the arrangement is in the TestFixtureSetUp, as is the action being tested. I would re-arrange this a bit, it may become unwieldy when you have more tests. As Dockers notes, heavy test set-ups should be avoided, they can become problems - they are "one size fits all" across all tests in the class and so can become heavier than most of the tests need.

If you're tempted to carry on to another follow-on action and then more asserts, put this in a separate test.

I have no issue with putting multiple asserts in the same test, so long as they contribute to testing the same thing (i.e are part of the same "Logical Assertion"). In this case, any number of asserts on the contents of this.Result.Data would be OK by me - they would all inspect the same result value. Your Get_UsingStaticSettings_HasData does this very clearly. It's best to use a unique failure message on each assert so that it's easier to tell which assert failed.

Alternately, you could wrap up the related asserts in a single method. this is useful for the usual DRY reasons if you use it more than once, but otherwise I don't see that it's a big difference.

In Summary
* Do one action per test
* After the action, use as many related asserts as you need to test one thing
* End the test there.

初见 2024-08-24 19:17:33

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

You can use Oapt - An NUnit Addin for Running One Assert Per Test:

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

This will create 5 different test cases, one for each assert.

幸福不弃 2024-08-24 19:17:33

我倾向于仅在断言本身有价值的情况下才单独提出断言。如果我想要一个单独的断言,我会创建一个自定义断言:

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 注释,如果类变得太复杂而难以理解,则将其拆分。

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

I tend to put assertions on their own only if they're valuable on their own. If I want an assertion on its own, I make a custom assertion:

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

Sometimes, though, I like to have more than one example in a test. I do this when half of the behaviour has no value without the other half.

Assert.True(list.IsEmpty());

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

Others, including much of the Ruby community, have a different opinion about this. It's primarily driven by Dave Astels' blog post, here:

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

I find the "One assertion per test" method very useful for things like validation, where each small aspect is valuable. Otherwise I don't worry about it so much.

Whatever works for you and your team is probably the right way. I tend to do whatever seems simple and easy to change to be the right thing later, when I have a better idea of what the right thing is. I also put lots of unit-level Given / When / Then comments in more complex examples, and split the class if it becomes too complex to understand.

The reason for writing tests this way isn't so that you can catch things which break. It's to help people understand the code and change it without breaking things in the first place.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文