FluentAssertions断言单个对象的多个属性

时间:2017-04-21 13:36:35

标签: c# .net-core fluent-assertions

有没有办法使用FluentAssertions

做这样的事情
response.Satisfy(r =>
    r.Property1== "something" &&
    r.Property2== "anotherthing"));

我试图避免编写多个Assert语句。使用https://sharptestex.codeplex.com/这是可能的,我使用的时间最长。但是SharpTestEx不支持.Net Core。

4 个答案:

答案 0 :(得分:9)

您应该能够使用通用Match断言通过谓词验证主题的多个属性

response.Should()
        .Match<MyResponseObject>((x) => 
            x.Property1 == "something" && 
            x.Property2 == "anotherthing"
        );

答案 1 :(得分:3)

以上(匹配)解决方案未返回正确的错误消息。因此,如果您想犯一个很好的错误,并且只断言,那么:

result.Should().BeEquivalentTo(new MyResponseObject()
            {
                Property1 = "something",
                Property2 = "anotherthing"
            });

匿名对象

如果您只想检查某些成员,请使用:

    result.Should().BeEquivalentTo(new
            {
                Property1 = "something",
                Property2 = "anotherthing"
            }, options => options.ExcludingMissingMembers());

小心使用!

注意:上述解决方案为您提供了一条断言。我认为多行断言没有错,只要它在功能上是一个断言即可。

如果因为一次要多个错误而需要这样做,请考虑将多行断言包装在AssertionScope中。

using (new AssertionScope())
{
    result.Property1.Should().Be("something");
    result.Property2.Should().Be("anotherthing");
}

如果它们均失败,则上述语句现在将立即给出两个错误。

https://fluentassertions.com/documentation/#assertion-scope

答案 2 :(得分:1)

为此,我使用了扩展功能,其功能类似于SatisfyRespectively()

public static class FluentAssertionsExt {
    public static AndConstraint<ObjectAssertions> Satisfy(
        this ObjectAssertions parent,
        Action<MyClass> inspector) {
        inspector((MyClass)parent.Subject);
        return new AndConstraint<ObjectAssertions>(parent);
    }
}

这是我的用法:

[TestMethod] public void FindsMethodGeneratedForLambda() =>
    Method(x => x.Lambda())
    .CollectGeneratedMethods(visited: empty)
    .Should().ContainSingle().Which
        .Should().Satisfy(m => m.Name.Should().Match("<Lambda>*"))
        .And.Satisfy(m => m.DeclaringType.Name.Should().Be("<>c"));

[TestMethod] public void FindsMethodGeneratedForClosure() =>
    Method(x => x.Closure(0))
    .CollectGeneratedMethods(visited: empty)
    .Should().HaveCount(2).And.SatisfyRespectively(
        fst => fst.Should()
            .Satisfy(m => m.Name.Should().Be(".ctor"))
            .And.Satisfy(m => m.DeclaringType.Name.Should().Match("<>c__DisplayClass*")),
        snd => snd.Should()
            .Satisfy(m => m.Name.Should().Match("<Closure>*"))
            .And.Satisfy(m => m.DeclaringType.Name.Should().Match("<>c__DisplayClass*")));

不幸的是,由于FluentAssertions的设计,这种方法不能很好地推广,因此您可能必须用不同的类型来提供此方法的多个重载,以代替MyClass

我认为,真正正确的方法是为要对其运行此类断言的类型实现*Assertions类型。该文档提供了an example

public static class DirectoryInfoExtensions 
{
    public static DirectoryInfoAssertions Should(this DirectoryInfo instance)
    {
      return new DirectoryInfoAssertions(instance); 
    } 
}

public class DirectoryInfoAssertions : 
    ReferenceTypeAssertions<DirectoryInfo, DirectoryInfoAssertions>
{
    public DirectoryInfoAssertions(DirectoryInfo instance)
    {
        Subject = instance;
    }

    protected override string Identifier => "directory";

    public AndConstraint<DirectoryInfoAssertions> ContainFile(
        string filename, string because = "", params object[] becauseArgs)
    {
        Execute.Assertion
            .BecauseOf(because, becauseArgs)
            .ForCondition(!string.IsNullOrEmpty(filename))
            .FailWith("You can't assert a file exist if you don't pass a proper name")
            .Then
            .Given(() => Subject.GetFiles())
            .ForCondition(files => files.Any(fileInfo => fileInfo.Name.Equals(filename)))
            .FailWith("Expected {context:directory} to contain {0}{reason}, but found {1}.", 
                _ => filename, files => files.Select(file => file.Name));

        return new AndConstraint<DirectoryInfoAssertions>(this);
    }
}

答案 3 :(得分:1)

假设您使用xUnit,则可以通过从正确的基类继承来解决它。无需在测试中更改实现。这是这样的:

public class UnitTest1 : TestBase
{
    [Fact]
    public void Test1()
    {
        string x = "A";
        string y = "B";
        string expectedX = "a";
        string expectedY = "b";
        x.Should().Be(expectedX);
        y.Should().Be(expectedY);
    }
}

public class TestBase : IDisposable
{
    private AssertionScope scope;
    public TestBase()
    {
        scope = new AssertionScope();
    }

    public void Dispose()
    {
        scope.Dispose();
    }
}

或者,您可以将期望包装到ValueTuple中。方法如下:

[Fact]
public void Test2()
{
    string x = "A";
    string y = "B";
    string expectedX = "a";
    string expectedY = "b";
    (x, y).Should().Be((expectedX, expectedY));
}