New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Should().Satisfy()
on objects and not only collections
#2516
Comments
We had a similar discussion in #2263, where I also added some additional use cases and benefits for having I like this idea 👍 A next step would be to dig into which |
@jnyrup I'm sorry, I didn't find that specific issue when I searched, but it does look similar to what I'm proposing here. |
Can you pick that up @siewers ? When we have the full list, we can approve the API. |
@dennisdoomen I'm a bit unsure how to proceed. I don't have an overview of the FluentAssertions architecture and design. I might simply start by "brute-forcing" the idea in and take it from there. It's usually the easiest way for me to learn. |
Sure, that could work. |
I've looked into it and I must admit it doesn't seem possible to do what I'm asking. Because I need the |
The closest thing I've been able to get is where I explicitly make the SomeType subject = new SomeType();
subject.Should()
.Satisfy<SomeType>(s => s.Property1.Should().NotBeEmpty());
/* Etc. */ The generic parameter on |
That's the only way I can think of to make this work. You would have to cast the |
In that case, I believe an additional overload to |
I've added similar tests as what is on the current Using predicates: someObject.Should().Match(o => o == null, "it is not initialized yet");
someObject.Should().Match((SomeDto d) => d.Name.Length == 0, "it is not initialized yet"); Using element inspectors: someObject.Should().Match<object>(o => o.Should().BeNull("it is not initialized yet"));
someObject.Should().Match<SomeDto>(d => d.Name.Should().HaveLength(0, "it is not initialized yet")); It is a bit longer, but it is a lot more expressive this way. I can write assertions on each property with the expectations and "because", which, in the end, produces a lot more meaningful output. It will also allow you to do more complex assertions: // Arrange / Act
var someObject = new SomeDto
{
Name = "Dennis Doomen",
Age = 36,
Birthdate = new DateTime(1973, 9, 20)
};
// Assert
someObject.Should().Match<SomeDto>(d =>
{
d.Name.Should().Be("Someone Else");
d.Age.Should().BeGreaterThan(40, because: "the person is at least 40");
d.Birthdate.Should().BeAfter(new DateTime(1982, 9, 22));
}); This will return the following error message:
I've also tested with nested assertions. It's a bit complex, but I find it a lot more readable (arguably, this is probably too much, but it's just there as an example). // Arrange / Act
var someObject = new SomeComplexDto
{
Person = new SomeDto
{
Name = "Buford Howard Tannen",
Age = 48,
Birthdate = new DateTime(1937, 3, 26)
},
Address = new AddressDto
{
Street = "Mason Street",
Number = "1809",
City = "Hill Valley",
Country = "United States",
PostalCode = "CA 91905"
}
};
// Assert
someObject.Should().Match<SomeComplexDto>(dto =>
{
dto.Person.Should().Match<SomeDto>(person =>
{
person.Name.Should().Be("Biff Tannen");
person.Age.Should().Be(48);
person.Birthdate.Should().Be(new DateTime(1937, 3, 26));
});
dto.Address.Should().Match<AddressDto>(address =>
{
address.Street.Should().Be("Mason Street");
address.Number.Should().Be("1809");
address.City.Should().Be("Hill Valley, San Diego County, California");
address.Country.Should().Be("United States");
address.PostalCode.Should().Be("CA 91905");
});
}); This will produce the following output:
|
Should().Satisfy()
on objects and not only collectionsShould().Satisfy()
on objects and not only collections
There's already a |
I did implement it as an overload for |
IIRC this lists all root Assertion types and from that it seems to me that implementing the API on only I vote for adding |
Let's go for |
Great! I'll introduce the On a side-note, I'm getting hit by an annoying bug in Rider, causing the tests to fail due to Rider trimming whitespaces inside raw string literals when saving. |
Yes, but as a separate commit or PR please. Also, add a nested class to the root of each partial class to provide a kind of grouping and context so you don't have to repeat words like match and satisfy. |
Sounds good. |
There are some guidelines in #1340 (pinned to the top of the issues page), but I agree it could be a bit better. |
I'm so sorry, I read that, but apparently, I wasn't thorough enough. I'll update the tests to reflect that. |
Background and motivation
When I'm asserting many properties on multiple instances, I often find it hard to keep track of the context of what I'm asserting.
Like with collections, we have a
collection.Should().AllSatisfy(item => { /* do FluentAssertions on item here */ }
, but I feel like this is missing for single instances.The closest thing I've found is
instance.Should().Match(/* something that returns true or false */)
, which is not really fluent as I see it.The alternative is to do object comparison, where I need to construct the expected object before and then do
instance.Should().BeEquivalentTo(expected)
.What I'm suggesting is to allow scoped assertions on a single instance, like this:
Providing this, also allows me to extract generic assertions as methods, which can be reused.
I am aware that the above is equivalent to this:
However, if I have many assertions where the variable names are long, it becomes quite a lot of noise in the tests.
For instance:
This would be, IMHO, more readable like this:
I am well aware that there might be a good reason for this to not exist and that it's possible to create a simple extension method like this:
I also understand that this approach could lead to confusion since the
Satisfy()
method would have to exist on all types being asserted.It might also be the case that my tests are too complex, however, it's an integration test, so I have a lot to assert, which makes the need clear to me.
Alternative Concerns
No response
Are you willing help with a pull-request?
Yes, please assign this issue to me.
The text was updated successfully, but these errors were encountered: