Skip to content

Commit

Permalink
Merge pull request #1140 from bfriesen/lambda_closure_support
Browse files Browse the repository at this point in the history
Improve error message when verification fails
  • Loading branch information
stakx committed Feb 2, 2021
2 parents 653db31 + e96804f commit f36d3e8
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,10 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1

* `CallBase` can now be used with interface methods that have a default interface implementation. It will call [the most specific override](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods#the-most-specific-override-rule). (@stakx, #1130)

#### Changed

* Improved error message formatting of `It.Is` lambda expressions that capture local variables. (@bfriesen, #1140)

#### Fixed

* `AmbiguousMatchException` raised when interface has property indexer besides property in VB. (@mujdatdinc, #1129)
Expand Down
22 changes: 20 additions & 2 deletions src/Moq/StringBuilderExtensions.AppendExpression.cs
Expand Up @@ -6,6 +6,8 @@
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;

using Moq.Properties;
Expand Down Expand Up @@ -68,7 +70,15 @@ public static StringBuilder AppendExpression(this StringBuilder builder, Express
return builder.AppendExpression((ConditionalExpression)expression);

case ExpressionType.Constant:
return builder.AppendValueOf(((ConstantExpression)expression).Value);
var constantValue = ((ConstantExpression)expression).Value;
if (constantValue is LambdaExpression lambda)
{
return builder.AppendExpression(lambda);
}
else
{
return builder.AppendValueOf(constantValue);
}

case ExpressionType.Parameter:
return builder.AppendExpression((ParameterExpression)expression);
Expand Down Expand Up @@ -251,7 +261,15 @@ private static StringBuilder AppendExpression(this StringBuilder builder, Member
{
if (expression.Expression != null)
{
builder.AppendExpression(expression.Expression);
if (expression.Expression is ConstantExpression ce
&& ce.Type.IsDefined(typeof(CompilerGeneratedAttribute)))
{
return builder.Append(expression.Member.Name);
}
else
{
builder.AppendExpression(expression.Expression);
}
}
else
{
Expand Down
35 changes: 35 additions & 0 deletions tests/Moq.Tests/VerifyFixture.cs
Expand Up @@ -2,6 +2,7 @@
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Linq.Expressions;
using System.Threading.Tasks;

using Moq;
Expand Down Expand Up @@ -65,6 +66,40 @@ public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherNotCalle
Assert.Contains(@".Execute(It.Is<string>(s => string.IsNullOrEmpty(s)))", mex.Message);
}

[Fact]
public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherVariableNotCalled()
{
var mock = new Mock<IFoo>();

Expression<Func<string, bool>> nullOrEmpty = s => string.IsNullOrEmpty(s);

mock.Setup(x => x.Execute(It.Is(nullOrEmpty)))
.Returns("ack")
.Verifiable();

var mex = Assert.Throws<MockException>(() => mock.Verify());
Assert.True(mex.IsVerificationError);
Assert.Contains(@".Execute(It.Is<string>(s => string.IsNullOrEmpty(s)))", mex.Message);
}

[Fact]
public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherVariableAndClosureAccessNotCalled()
{
var mock = new Mock<IFoo>();

string password = "abc123";

Expression<Func<string, bool>> matchingPassword = s => s == password;

mock.Setup(x => x.Execute(It.Is(matchingPassword)))
.Returns("ack")
.Verifiable();

var mex = Assert.Throws<MockException>(() => mock.Verify());
Assert.True(mex.IsVerificationError);
Assert.Contains(@".Execute(It.Is<string>(s => s == password)", mex.Message);
}

[Fact]
public void VerifiesNoOpIfNoVerifiableExpectations()
{
Expand Down

0 comments on commit f36d3e8

Please sign in to comment.