FluentAssertions 断言单个对象的多个属性
FluentAssertions Asserting multiple properties of a single object
有没有办法使用 FluentAssertions 做这样的事情
response.Satisfy(r =>
r.Property1== "something" &&
r.Property2== "anotherthing"));
我试图避免编写多个 Assert 语句。这可以通过我使用时间最长的 https://sharptestex.codeplex.com/ 来实现。但是SharpTestEx不支持.Net Core
您应该能够使用通用 Match
断言通过谓词验证主题的多个属性
response.Should()
.Match<MyResponseObject>((x) =>
x.Property1 == "something" &&
x.Property2 == "anotherthing"
);
.Match()
解决方案没有 return 好的错误消息。所以如果你想有一个好的错误并且 只有一个 断言然后使用:
result.Should().BeEquivalentTo(new MyResponseObject()
{
Property1 = "something",
Property2 = "anotherthing"
});
匿名对象(慎用!)
如果您只想检查某些成员,请使用:
result.Should().BeEquivalentTo(new
{
Property1 = "something",
Property2 = "anotherthing"
}, options => options.ExcludingMissingMembers());
Note: You will miss (new) members when testing like this. So only use if you
really want to check only certain members now and in the future. Not
using the exclude option will force you to edit your test when a new
property is added and that can be a good thing
多个断言
Note: All given solutions gives you one line asserts. In my opinion there is nothing wrong with multiple lines of asserts as long
as it is one assert functionally.
如果您想要这样做是因为您希望同时出现多个错误,请考虑将您的多行断言包装在 AssertionScope
.
中
using (new AssertionScope())
{
result.Property1.Should().Be("something");
result.Property2.Should().Be("anotherthing");
}
上面的语句现在将同时给出两个错误,如果它们都失败了。
我为此使用了一个扩展函数,其工作方式类似于 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);
}
}
假设你使用xUnit,你可以通过从正确的基础继承来解决它class。无需在测试中更改实现。这是它的工作原理:
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 做这样的事情
response.Satisfy(r =>
r.Property1== "something" &&
r.Property2== "anotherthing"));
我试图避免编写多个 Assert 语句。这可以通过我使用时间最长的 https://sharptestex.codeplex.com/ 来实现。但是SharpTestEx不支持.Net Core
您应该能够使用通用 Match
断言通过谓词验证主题的多个属性
response.Should()
.Match<MyResponseObject>((x) =>
x.Property1 == "something" &&
x.Property2 == "anotherthing"
);
.Match()
解决方案没有 return 好的错误消息。所以如果你想有一个好的错误并且 只有一个 断言然后使用:
result.Should().BeEquivalentTo(new MyResponseObject()
{
Property1 = "something",
Property2 = "anotherthing"
});
匿名对象(慎用!)
如果您只想检查某些成员,请使用:
result.Should().BeEquivalentTo(new
{
Property1 = "something",
Property2 = "anotherthing"
}, options => options.ExcludingMissingMembers());
Note: You will miss (new) members when testing like this. So only use if you really want to check only certain members now and in the future. Not using the exclude option will force you to edit your test when a new property is added and that can be a good thing
多个断言
Note: All given solutions gives you one line asserts. In my opinion there is nothing wrong with multiple lines of asserts as long as it is one assert functionally.
如果您想要这样做是因为您希望同时出现多个错误,请考虑将您的多行断言包装在 AssertionScope
.
using (new AssertionScope())
{
result.Property1.Should().Be("something");
result.Property2.Should().Be("anotherthing");
}
上面的语句现在将同时给出两个错误,如果它们都失败了。
我为此使用了一个扩展函数,其工作方式类似于 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);
}
}
假设你使用xUnit,你可以通过从正确的基础继承来解决它class。无需在测试中更改实现。这是它的工作原理:
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));
}