Skip to content
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

Is there any way we could make use of CallerArgumentExpressionAttribute? #3936

Closed
Dreamescaper opened this issue Sep 19, 2021 · 7 comments
Closed

Comments

@Dreamescaper
Copy link
Member

C#10 has a nice addition of CallerArgumentExpressionAttribute, which we could use to print actual argument expression in error messages.

I can see two problems though:

  • We need to add optional string parameter to methods. But we already have overloads with message string parameter.
  • We have methods with params parameter. And we can't combine params and optional parameters in the same method.

Is there any way we could make use of this attribute?

@manfred-brands
Copy link
Member

We cannot do this with breaking binary compatibility, but the attribute is only effective at compile time anyhow.
For source code we have currently two methods for each test, e.g.:

Assert.True(bool condition)
Assert.True(bool condition, string? message, param object?[]? args)

We can replace the first with:

Assert.True(bool condition, [CallerArgumentExpression("condition")] string? message = null)

It will not be confused with the 2nd. Similar like Console.WriteLine has different overloads.

@Dreamescaper
Copy link
Member Author

Problem is that existing code like that:

Assert.True(myCondition, "MyCondition should certainly be true!");

will pick up the first method now.

Assert.True(bool condition, [CallerArgumentExpression("condition")] string? conditionExpression = null)

@manfred-brands
Copy link
Member

manfred-brands commented Sep 20, 2021

Which is correct. The CallerArgumentExpression only evaluates to the condition string when not specified, otherwise it will be the value passed as an argument:

Assert.True(args.Length == 0);
Assert.True(args.Length == 0, "args");
Assert.True(args.Length == 0, "{0} != 0", args.Length);

Gives:

Expected 'true' for 'args.Length == 0'
Expected 'true' for 'args'
Expected 'true' for '1 != 0'

Based on the following dummy class:

public static class Assert
{
    public static void True(bool condition, [CallerArgumentExpression("condition")] string? message = null)
    {
        if (!condition)
        {
            Console.Error.WriteLine($"Expected 'true' for '{message}'");
        }
    }

    public static void True(bool condition, string message, params object?[] args)
    {
        if (!condition)
        {
            Console.Error.WriteLine($"Expected 'true' for '{string.Format(message, args)}'");
        }
    }
}

@Dreamescaper
Copy link
Member Author

Problem is that existing method usages rarely provide actual expression, but usually some failure description.
So for my example

Assert.True(myCondition, "MyCondition should be true when the sky is blue!");

it will print

Expected 'true' for 'MyCondition should be true when the sky is blue!'

which is probably not what's expected.

@manfred-brands
Copy link
Member

Yes, this only helps where there was no message given previously.

@manfred-brands
Copy link
Member

This one helps with your situation:

public static void True(bool condition, string? userMessage = null, [CallerArgumentExpression("condition")] string? conditionMessage = null)

This however will conflict in one case with the params overload if a user passes a string argument as params, e.g.:
Assert.True(variable, "Expected: '{0}' to be true.", nameof(variable));

Potentially this can be detected by scanning for '{0' in the string.

@jamers99
Copy link

jamers99 commented Dec 9, 2022

This would be a really great feature to have. Is there any option to do this ourselves?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants