3

Is there anyway to specify what gets appended to the {context} key of the built in assertions or potentially get the value of that context?

Sample

E.g. I managed to append to custom Assertions using AssertionSope keys to custom Execution.Assertions as follows:

Sample Classes

public class Foo
{
    public Bar Bar { get; } = new Bar();
}

public class Bar
{
    public bool IsFull {get; set; }
}

Extension Classes

public static class Extensions
{
    public static FooAssertions Should(this Foo foo) => new FooAssertions(foo);
    public static BarAssertions Should(this Bar bar) => new BarAssertions(bar);

    public class FooAssertions
    {
        public Foo Subject { get; }

        public FooAssertions(Foo subject)
        {
            Subject = subject;
        }

        [CustomAssertion]
        public void HaveFullBar(string because = null, params object[] becauseArgs)
        {
            using (var scope = new AssertionScope())
            {
                scope.AddReportable("SubPath", "." + nameof(Foo.Bar));

                Subject.Bar.Should().BeFull(because, becauseArgs);
            }
        }
    }

    public class BarAssertions
    {
        public Bar Subject { get; }

        public BarAssertions(Bar subject)
        {
            Subject = subject;
        }

        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            Execute.Assertion
                .ForCondition(Subject.IsFull)
                .BecauseOf(because, becauseArgs)
                .FailWith("Expected {context}{SubPath}.IsFull to be {0}{reason} but found {1}", true, Subject.IsFull);
        }
    }
}

Test Method

    [TestMethod]
    public void CustomTest()
    {
        var foo = new Foo();

        foo.Should().HaveFullBar();
    }

Output

Output: Expected foo.Bar.IsFull to be True but found False

Question

How can I append to the build in {context} so that I don't have to re-invent the wheel for every single assertion FluentAssertion already provides?

E.g. calling the standard BooleanAssertions.Should().BeTrue():

        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            Subject.IsFull.Should().BeTrue();
        }

but this loses the SubPath of .Bar.IsFull and outputs:

 Output: Expected foo to be true, but found False.

Attempts

Attempt One

Tried to set the "context" of inner assertion scopes to some value, but it loses the previous values!

        [CustomAssertion]
        public void HaveFullBar(string because = null, params object[] becauseArgs)
        {
            using (new AssertionScope(".Bar"))
            {
                Subject.Bar.Should().BeFull(because, becauseArgs);
            }
        }

        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            using (new AssertionScope(".IsFull"))
            {
                Subject.IsFull.Should().BeTrue();
            }
        }

This just returns .IsFull which is the most inner AssertionScope:

Expected .IsFull to be true, but found False.

Attempt Two

Tried to add "{context}" into the constructor of AssertionScope

        [CustomAssertion]
        public void HaveFullBar(string because = null, params object[] becauseArgs)
        {
            using (new AssertionScope("{context}.Bar"))
            {
                Subject.Bar.Should().BeFull(because, becauseArgs);
            }
        }
        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            using (new AssertionScope("{context}.IsFull"))
            {
                Subject.IsFull.Should().BeTrue();
            }
        }

This just outputs the most inner {context}.IsFull

 Expected {context}.IsFull to be true, but found False.

Attempt Three

        [CustomAssertion]
        public void HaveFullBar(string because = null, params object[] becauseArgs)
        {
            using (new AssertionScope(AssertionScope.Current.Context + ".Bar"))
            {
                Subject.Bar.Should().BeFull(because, becauseArgs);
            }
        }

        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            using (var scope = new AssertionScope(AssertionScope.Current.Context + ".IsFull"))
            {
                Subject.IsFull.Should().BeTrue();
            }
        }

Still loses original context, as the first AssertionScope.Current.Context is null;

Output:

Expected .Bar.IsFull to be true, but found False.
Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • Do you have those attempts somewhere in a Git repo that I can check out? – Dennis Doomen May 23 '18 at 06:49
  • 1
    @DennisDoomen Not right now, I will probably raise an issue in the FA repo, with these tests in the fork repository if that is ok? – Michal Ciechan May 23 '18 at 09:51
  • 1
    @DennisDoomen Did this evolved at all? I'm trying to create some custom assertions myself and hit the same "issue". Excellent question Michal Ciechan. Thanks to you I found much of what I was looking for.... – Kelps Sousa Alux Nov 30 '21 at 19:52
  • I override the Identifier property in my assertions class to change the context, but not sure if this will work in your case as it looks like you want to change it per method. – MrKWatkins Sep 04 '22 at 11:07

0 Answers0