FluentAssertions 断言单个对象的多个属性
Posted
技术标签:
【中文标题】FluentAssertions 断言单个对象的多个属性【英文标题】:FluentAssertions Asserting multiple properties of a single object 【发布时间】:2017-09-18 13:02:06 【问题描述】:有没有办法使用 FluentAssertions 来做这样的事情
response.Satisfy(r =>
r.Property1== "something" &&
r.Property2== "anotherthing"));
我试图避免编写多个 Assert 语句。我使用时间最长的https://sharptestex.codeplex.com/ 可以做到这一点。但是 SharpTestEx 不支持 .Net Core。
【问题讨论】:
这应该怎么做? 在撰写本文时,公认的答案可能是最好的,不再是...... 【参考方案1】:您应该能够使用通用的Match
断言通过谓词验证主题的多个属性
response.Should()
.Match<MyResponseObject>((x) =>
x.Property1 == "something" &&
x.Property2 == "anotherthing"
);
【讨论】:
虽然此代码有效,但断言失败时的错误消息非常尴尬。与 FluentAssertions 通常产生的结果相去甚远。我建议改用多个断言:) @the_joric 或使用ShouldBeEquivalentTo
【参考方案2】:
.Match()
解决方案没有返回好的错误消息。因此,如果您想要一个好的错误并且只有一个断言,那么请使用:
result.Should().BeEquivalentTo(new MyResponseObject()
Property1 = "something",
Property2 = "anotherthing"
);
匿名对象(谨慎使用!)
如果您只想检查某些成员,请使用:
result.Should().BeEquivalentTo(new
Property1 = "something",
Property2 = "anotherthing"
, options => options.ExcludingMissingMembers());
注意:在进行这样的测试时,您会错过(新)成员。所以只有当你使用 现在和将来真的只想检查某些成员。不是 使用 exclude 选项将强制您在新的测试 添加了属性,这可能是一件好事
多个断言
注意:所有给定的解决方案都会给你一行断言。在我看来,多行断言没有错,只要 因为它在功能上是一个断言。
如果您想要这样做是因为您想要一次出现多个错误,请考虑将您的多行断言包装在 AssertionScope
中。
using (new AssertionScope())
result.Property1.Should().Be("something");
result.Property2.Should().Be("anotherthing");
上面的语句现在会同时给出两个错误,如果它们都失败了。
https://fluentassertions.com/introduction#assertion-scopes
【讨论】:
你能详细说明这个“匿名对象(小心使用!)”吗? @Menyus 是的,在进行这样的测试时,您会错过(新)成员。因此,只有在您现在和将来真的想只检查某些成员时才使用。不使用 exclude 选项将迫使您在添加新属性时编辑测试,这可能是一件好事。【参考方案3】:我为此使用了一个与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 0reason, but found 1.",
_ => filename, files => files.Select(file => file.Name));
return new AndConstraint<DirectoryInfoAssertions>(this);
【讨论】:
【参考方案4】:假设您使用 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));
【讨论】:
以上是关于FluentAssertions 断言单个对象的多个属性的主要内容,如果未能解决你的问题,请参考以下文章