fluentassertions icon indicating copy to clipboard operation
fluentassertions copied to clipboard

Extend BooleanAssertions with Implies

Open marcrocny opened this issue 3 years ago • 4 comments

Description

Add logical implication to the available boolean assertions.

That is, a.Should().Imply(b) asserts that a → b.

This would be useful in asserting, for example, that object equality implies hash-code equality without additional logic and a better built-in "because".

Complete minimal example

Looking for something like this.

[DataTestMethod]
[DataRow(false, false)]
[DataRow(false, true)]
// failure case: [DataRow(true, false)]
[DataRow(true, true)]
public void A_Implies_B(bool a, bool b) => a.Should().Imply(b);

Note this is logically equivalent to !a || b.

Additional Information

Also volunteering to add this.

marcrocny avatar Apr 11 '22 19:04 marcrocny

Sorry, I don't get it.

dennisdoomen avatar Apr 12 '22 16:04 dennisdoomen

Which part don't you get?

a → b is called implication and is read "a implies b" and is logically equivalent to !a || b.

https://en.m.wikipedia.org/wiki/Material_conditional

jnyrup avatar Apr 12 '22 16:04 jnyrup

This is where my lack of CS degree shows ;-)

dennisdoomen avatar Apr 12 '22 16:04 dennisdoomen

The api and implementation are both straight forward, but my immediate gut feeling is that it will only be needed/used by a few.

If you haven't done it already, you could create Imply as an extension method on BooleanAssertions in your code base.

jnyrup avatar Apr 12 '22 17:04 jnyrup

@jnyrup i will take a look on it and try to make the extension.

94sedighi avatar Nov 28 '22 12:11 94sedighi

@94sedighi Please wait until the API is approved before opening a PR.

jnyrup avatar Nov 28 '22 13:11 jnyrup

Albeit a bit of a niche request, I have no issue with this proposal. It doesn't add complexity, so it should be easy to support.

dennisdoomen avatar Dec 26 '22 17:12 dennisdoomen

Alright, then I'll approve the API addition too.

public class BooleanAssertions<TAssertions>
{
    public AndConstraint<TAssertions> Imply(bool expected, string because = "", params object[] becauseArgs)
}

jnyrup avatar Dec 27 '22 18:12 jnyrup

Is there a way to extract the expectation variable name?

This seems a little odd in the failure message: bla.Should().Imply(blubb) would result in Expected "bla" to imply "[variable name inside the method]", but it did not.

But IMO the failure message should contain both, like: Expected "bla" to imply "blubb", but it did not.

IT-VBFK avatar Dec 28 '22 09:12 IT-VBFK

Is there a way to extract the expectation variable name?

Not currently. We would like to look into using [CallerArgumentExpression] for this purpose. This is listed in #1677.

jnyrup avatar Dec 28 '22 09:12 jnyrup

Ahhm.. @94sedighi I've missed the comment where you "saved" this issue.

Sorry :)

IT-VBFK avatar Dec 28 '22 11:12 IT-VBFK

Ahhm.. @94sedighi I've missed the comment where you "saved" this issue.

Sorry :)

Allright 😉

94sedighi avatar Dec 28 '22 15:12 94sedighi

We would like to look into using [CallerArgumentExpression] for this purpose.

I think we can already use that in this API

dennisdoomen avatar Dec 28 '22 16:12 dennisdoomen

How would you add this argument?

public AndConstraint<TAssertions> Imply(bool implicator,
        string because = "",
        [CallerArgumentExpression("implicator")] string implicatorMessage = "",
        params object[] becauseArgs)

or

public AndConstraint<TAssertions> Imply(bool implicator,
        [CallerArgumentExpression("implicator")] string implicatorMessage = "",
        string because = "",
        params object[] becauseArgs)

IT-VBFK avatar Dec 28 '22 17:12 IT-VBFK

Hmm.. what I can see by now: we have to go with approach 2, because if you pass becauseArgs the first one is considered as custom CallerArgumentExpression

IT-VBFK avatar Dec 28 '22 17:12 IT-VBFK

Using approach 2 is also problematic

Writing this idiomatic FA code

subject.Should().Imply(implicator, "because we want to test the {0}", "failure");

fails with this unexpected message

Expected subject (True) to imply "because we want to test the {0}" (False) because failure, but it did not.

jnyrup avatar Dec 29 '22 18:12 jnyrup

Ok I see. Unfortunately this param has to be optional.

What we can do? Can we force users to use this parameter?

IT-VBFK avatar Dec 30 '22 08:12 IT-VBFK

Or maybe this only a documentation issue?

IT-VBFK avatar Dec 30 '22 09:12 IT-VBFK

I think we should stick with the approved API, i.e. not using [CallerArgumentExpression]. Other APIs that might also benefit from having the name of the expectation, like ReferenceTypeAssertions.BeSameAs, would face the same problem.

jnyrup avatar Dec 30 '22 11:12 jnyrup

ok.. I am fine with that.

IT-VBFK avatar Dec 30 '22 11:12 IT-VBFK

Ok I see. Unfortunately this param has to be optional.

Ah, I see now. So yes, I agree with @jnyrup

dennisdoomen avatar Jan 01 '23 10:01 dennisdoomen