Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking out expression with NSubstitute

I have a interface that contains the following method signature:

TResult GetValue<T, TResult>(object key, Expression<Func<T, TResult>> property) where T : class;

Using Moq, I'm able to mock a specific call of this method like this:

var repo = new Mock<IRepository>();
repo.Setup(r => r.GetValue<Customer, string>("SomeCustomerId", c => c.SecretAgentId)).Returns("SecretAgentId");

Then when I do this call

repo.Object.GetValue<Customer, string>("SomeCustomerId", c => c.SecretAgentId);

Tt returns "SecretAgentId" as I expect, so everything looks fine.

My problem is that in our real production code we use NSubstitute, and not Moq. I tried using the same type of setup here:

var repo = Substitute.For<ICrmRepository>();
repo.GetValue<Customer, string>("SomeCustomerId", c => c.SecretAgentId).Returns("SecretAgentId");

However, when I do the following call here

repo.GetValue<Customer, string>("SomeCustomerId", c => c.SecretAgentId);

It returns "" instead of "SecretAgentId"

I tried replacing c => c.SecretAgentId with Arg.Any<Expression<Func<Customer, string>>>() just to see if it works then, and then it returns "SecretAgentId" as expected. But I need to verify that it is called with the correct expression, and not just any expression.

So I need to know if it is possible to get this to work in NSubstitute, and if it is, how?

like image 453
Øyvind Bråthen Avatar asked Mar 19 '23 08:03

Øyvind Bråthen


2 Answers

I think that expressions are evaluated in NSubstitute depending on their closure scope, so the two expressions declarations are not identical. It looks like a bug to me, you may want to open an issue.

You can however take the expression out of the substitution declaration and it works correctly:

private static void Main(string[] args)
{
    Expression<Func<string, string>> myExpression =  s => s.Length.ToString();
    var c = Substitute.For<IRepo>();
    c.GetValue<string, string>("c", myExpression).Returns("C");

    var result = c.GetValue<string, string>("c", myExpression); // outputs "C"
}
like image 170
samy Avatar answered Mar 22 '23 22:03

samy


I can't remember exact syntax, so forgive me if this isn't A1 correct, and it is a bit kludgy but...

I believe you were on the right track when you tried Arg.Any, however try using Arg.Is like this:

Arg.Is<Expression<Func<Customer, string>>>(x => { 
    var m = ((Expression)x).Body as MemberExpression;
    var p = m.Member as PropertyInfo;
    return p.Name == "SecretAgentId";
});
like image 21
Fordio Avatar answered Mar 22 '23 23:03

Fordio