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
[WIP] Add more specific variants of contain (#818) #962
Conversation
If I should change something in assumptions please comment and I will fix it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See http://dawehner.github.io/github,/code/review/2017/09/08/emoji-code-review.html for the meaning of the emojis.
//----------------------------------------------------------------------------------------------------------- | ||
// Act | ||
//----------------------------------------------------------------------------------------------------------- | ||
Action act = () => actual.Should().Contain(expectedSubstring).AtMost.Once("that is {0}", "required"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ This is a prime example of why I don't like this augmentation of Contain
.
ABCDEF
contains XYS
0 times, which is less than 1, but the test fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, that was my assumption.
I think that you as maintainers should decide about API.
I see following solutions:
- Leave it as is
- Replace the current
Contain
by something like thatactual.Should().Contain("Fred").Any
- Expand
Contain
in different way, e.g. as you suggested in Add version of StringAssertions.Contain that allows specifying the number of times a substring should occur. #818 - Introduce new method, but I don't know how to name it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dennisdoomen Here's my beef with this API.
For inspiration, FakeItEasy has shared their considerations, when they changed their API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem they were facing had to do with the word repeated. We don't have this issue with this API. It reads very fluently IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dennisdoomen I agree that it reads fluently, but the result of
"A".Should().Contain("B").AtMost.Once();
would fail and be logically inconsistent with the written assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, either we make sure our API does not support that, or we reconsider the API
} | ||
|
||
[Fact] | ||
public void When_string_containment_at_most_is_asserted_and_actual_value_contains_the_expected_string_but_not_at_most_expected_times_it_should_throw() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x This is a prime example of why I don't like this augmentation of Contain
.
"ABCDEF" contains "XYS" 0 times, which is less than 1, but the test fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same as in #962 (comment)
With the lessons learned from #925 this PR would break ABI compatibility by changing the return of a public member. We cannot add StringContainmentConstraints Contain(string expected, ...) as another overload, as the return type is not part of the function signature and would cause a conflict with the existing AndConstraint<StringAssertions> Contain(string, ...) Here are my thoughts on the API.
From all the examples I could think about, 2.b is the clear winner to me.
All would cause ABI breakage if
2.a is not very pretty if generalized, e.g.
Both are polluting Should() with AtLeast, AtMost, Exactly, MoreThan, LessThan
Both are polluting Should() with AtLeast, AtMost, Exactly, MoreThan, LessThan note 1: consider e.g. |
What if the return type inherits from the original return type? |
I just tried and changing the return type to a child class of the original return type breaks binary compatibility. |
So what do we want to do with this PR? |
I would prefer if this was reusable, such that other E.g.
|
That's a different concern, right? As far as I remember, the discussion ended with the breaking nature of this PR. IMO, this is something we should avoid. |
Hmm. Looking through the entire discussion, I feel (1) is still the best API but will cause breaking changes. (2) doesn't have that problem, but may cause naming conflicts with people that are using FakeitEasy as well. I definitely don't like (3) and (4). |
It's an additional concern. "aaa".Should().Contain("a") // <-- would get support from this PR
new int[] { 1, 1, 1 }.Should().Contain(1) // <-- wouldn't |
@mkolumb: @dennisdoomen and I have discussed the API and we found that your earlier API proposal ended up being the best non-breaking logically consistent way to implement this feature. public delegate bool MatchesOccurenceCount(int actual);
public AndConstraint<StringAssertions> Contain(string expected, MatchesOccurenceCount predicate, string because = "", params object[] becauseArgs) // Usage
actual.Should().Contain("Fred", AtLeast.Twice, "Fred is important");
// Predefined delegates
{AtLeast, AtMost, MoreThan, LessThan, Exactly}.{Once, Twice, Times(int count)}
(Maybe leave out weird ones such at LessThan.Once?)
// Custom predicates
actual.Should().Contain("Fred", count => count % 2 == 0, "Fred must appear an equal number of times"); |
Ok, I will change it, probably next weekend. |
Sorry for the confusion. But deciding on the trade-off between a nice fluent and intuitive API and backwards compatibility proved to be less than trivial. |
Is this still on your radar? |
Yes, sorry, I didn't have a time to finish it. I will do this this week |
I'm very interested in how good failure messages we can get. |
@mkolumb can we still motivate you to finish this? |
Since there has been no activity for 6 months, let's close it. |
Hi,
I have implemented this enhancement.
You can find the API in documentation.md
Because I didn't receive a response, I made the following assumptions:
Contain
should throw always if expected string wouldn't be found (as in the previous version)aaa
containsaa
once,aaaaa
containsaa
twice