如何使用断言作用域在 C# 中执行多个断言

在 C# 中使用断言作用域执行多个断言

Fluent Assertions是一个 .NET 库,它提供了一系列有用的扩展方法,使我们能够以更自然的方式测试我们的 C# 代码。

例如,假设我们正在测试字符串的输出。如果没有 Fluent Assertions,我们可能会这样写:

string testString = "hello";
string expectedOutput = testString.ToUpper();
Assert.Equal(expectedOutput, "HELLO");

如果我们要使用 Fluent Assertions 编写这个测试,我们可以这样做:

string testString = "hello";
string expectedOutput = testString.ToUpper();
expectedOutput.Should().Be("HELLO");

看?更自然?

引入断言范围

让我们使用一个更广泛的例子。假设我有一个生成购物清单的类,如下所示:

public class ShoppingListGenerator
    {
        public static List<Item> GenerateItems()
        {
            return new List<Item>
            {
                new Item
                {
                    Name = "Apple",
                    Quantity = 5
                },
                new Item
                {
                    Name = "Banana",
                    Quantity = 1
                },
                new Item
                {
                    Name = "Orange",
                    Quantity = 3
                }
            };
        }
    }

对于更复杂的单元测试,我们可能希望对多个属性进行断言,如下所示:

public class ShoppingListGeneratorShould
    {
        [Fact]
        public void MultipleAssertions()
        {
            var testShoppingList = ShoppingListGenerator.GenerateItems();

            testShoppingList.Should().NotBeNullOrEmpty();
            testShoppingList.Should().Contain(new Item { Name = "Cheese", Quantity = 2 });
            testShoppingList.Should().HaveCount(10);
            testShoppingList.Should().OnlyHaveUniqueItems();           
        }
    }

这种方法很好,但是查看我们的代码,我们可以看到这个测试会因为断言我们的列表将在 .Contain() 方法上失败,因为我们的列表中没有包含 Cheese 的项目。这个测试也会在我们的 .HaveCount() 方法上失败,因为我们的列表中只有 3 个项目,而不是 10 个。

让我们通过运行测试来确认我们的想法。

我们是对的!但是在这个例子中,我们的测试不仅对我们的 .Contains() 方法失败了,而且它已经停止运行我们的测试!

想象一下,我们修复了这个错误,使得断言通过,但在下一个断言中失败。然后是下一个,然后是下一个,依此类推。

像这样测试我们的代码会相当乏味。幸运的是,我们可以使用 Assertion Scope 来解决这个问题!

使用 Assertion Scopes,我们可以将多个断言批处理到一个 AssertionScope 中,这样 FluentAssertions 只会在所有失败的作用域结束时抛出一个异常。

让我们更改我们的测试以使用 Assertion Scope。

using AssertionScopes;
using FluentAssertions;
using FluentAssertions.Execution;
using System;
using Xunit;

namespace Tests
{
    public class ShoppingListGeneratorShould
    {
        [Fact]
        public void MultipleAssertions()
        {
            var testShoppingList = ShoppingListGenerator.GenerateItems();

            using (new AssertionScope())
            {
                testShoppingList.Should().NotBeNullOrEmpty();
                testShoppingList.Should().Contain(new Item { Name = "Cheese", Quantity = 2 });
                testShoppingList.Should().HaveCount(10);
                testShoppingList.Should().OnlyHaveUniqueItems();
            }                     
        }
    }
}

我们现在将我们的断言包装在 using 语句中,并且我们的异常只会在处置我们的 AssertionScope 时被抛出。

让我们运行测试,看看会发生什么!

我们的测试失败了,但现在我们将两个异常都抛给了我们!

Message: 
    Expected testShoppingList {AssertionScopes.Item
       {
          Name = "Apple"
          Quantity = 5
       }, AssertionScopes.Item
       {
          Name = "Banana"
          Quantity = 1
       }, AssertionScopes.Item
       {
          Name = "Orange"
          Quantity = 3
       }} to contain 

    AssertionScopes.Item
    {
       Name = "Cheese"
       Quantity = 2
    }.
    Expected testShoppingList to contain 10 item(s), but found 3.

现在,不必每次测试中的断言失败时都运行并重新运行我们的测试,我们可以立即查看测试中失败的所有断言!

结论

Fluent Assertions 是一个很棒的库,它允许我们以更自然和更有表现力的方式编写 C# 测试。Assertion Scopes 通过在找出测试失败的原因时节省我们的时间和精力,让我们在单元测试中使用多个断言时变得更轻松。

如果您想了解有关 Fluent 断言的更多信息,请在此处查看文档:https ://fluentassertions.com/about/