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

cannot mock a method to return null using LINQ syntax #337

Closed
mayerc-MSFT opened this issue Mar 29, 2017 · 4 comments
Closed

cannot mock a method to return null using LINQ syntax #337

mayerc-MSFT opened this issue Mar 29, 2017 · 4 comments

Comments

@mayerc-MSFT
Copy link

I need the result of a method vary by argument and return null for a particular argument. I tried the following snippet but Mock.Of fails (exception details further below.)

var mockUserProvider = Mock.Of<IUserProvider>(p => p.GetUserByEmail("john@contoso.com") == new User() && p.GetUserByEmail("alice@fabrikam.com") == null);

If I remove the second clause of the predicate, then the exception doesn't happen.

This feels like something that should work. Could someone offer a work-around?

Not sure if this issue is the same as this: https://groups.google.com/forum/#!topic/moqdisc/tRYXrkgTjI0

exception details

{"Expression of type 'System.Object' cannot be used for parameter of type 'Kobe.Common.Models.Interfaces.IUserWithDetails' of method 'Moq.Language.Flow.IReturnsResult`1[Kobe.Common.Users.IUserProvider] Returns(Kobe.Common.Models.Interfaces.IUserWithDetails)'"}

" at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)\r\n at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection1& arguments)\r\n at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable1 arguments)\r\n at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments)\r\n at Moq.Linq.MockSetupsBuilder.ConvertToSetup(Expression targetObject, Expression left, Expression right)\r\n at Moq.Linq.MockSetupsBuilder.ConvertToSetup(Expression left, Expression right)\r\n at Moq.Linq.MockSetupsBuilder.VisitBinary(BinaryExpression node)\r\n at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)\r\n at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)\r\n at Moq.Linq.MockSetupsBuilder.VisitBinary(BinaryExpression node)\r\n at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)\r\n at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression1 node)\r\n at System.Linq.Expressions.Expression1.Accept(ExpressionVisitor visitor)\r\n at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)\r\n at Moq.Linq.MockSetupsBuilder.VisitUnary(UnaryExpression node)\r\n at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)\r\n at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)\r\n at Moq.Linq.MockSetupsBuilder.VisitMethodCall(MethodCallExpression node)\r\n at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)\r\n at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)\r\n at Moq.Linq.MockQueryable1.Execute[TResult](Expression expression)\r\n at System.Linq.Queryable.First[TSource](IQueryable1 source, Expression1 predicate)\r\n at Moq.Mock.Of[T](Expression1 predicate)\r\n at Kobe.Web.Controllers.Api.Tests.UserControllerTests.<Patch_Accepts_Addition_Of_Email_Address_Alias_When_User_Has_No_Aliases>d__17.MoveNext() in

@kzu
Copy link
Member

kzu commented Mar 29, 2017 via email

@mayerc-MSFT
Copy link
Author

hey kzu, thanks for the quick response.

Since null is the default return value for such a mock, why even try
setting it explicitly?

Because it makes the intent clear in the code, therefore the code is easier to read. It's easier to read for myself but more importantly to members of my team, some of which may not have much experience with Moq.

The actual code I'd like to write is more like this:
const User NonExistingUser = null;

var mockUserProvider = Mock.Of<IUserProvider>(p => p.GetUserByEmail("john@contoso.com") == new User() && p.GetUserByEmail("alice@fabrikam.com") == NonExistingUser);

In the above code, the intent (return "non existing user" when GetUserByEmail is invoked with "alice@fabrikam.com") is very clear, hence easier to understand than the assumption that the default is null.

@stakx
Copy link
Contributor

stakx commented Jun 5, 2017

@mayerc-MSFT, you can actually do that!

public partial class User { }

public interface IUserProvider
{
    User GetUserByEmail(string email);
}User nonExistingUser = null;

var provider = Mock.Of<IUserProvider>(p => p.GetUserByEmail("john@contoso.com")   == new User() 
                                        && p.GetUserByEmail("alice@fabrikam.com") == nonExistingUser);

Assert.IsType<User>(provider.GetUserByEmail("john@contoso.com"));
Assert.Equal(nonExistingUser, provider.GetUserByEmail("alice@fabrikam.com"));

(Don't declare nonExistingUser as const, though.)

@stakx
Copy link
Contributor

stakx commented Jun 28, 2017

(Don't declare nonExistingUser as const, though.)

This warning is good for now, but you can ignore it with the next release (version >4.7.63) of Moq.

@stakx stakx closed this as completed Jul 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants