From 5b10a8ce3fc7c21e8b3c4e04a51423f9f9bfad0b Mon Sep 17 00:00:00 2001 From: Brian Friesen Date: Mon, 1 Feb 2021 13:30:21 -0500 Subject: [PATCH 1/5] Add test for lamba matcher variables --- tests/Moq.Tests/VerifyFixture.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Moq.Tests/VerifyFixture.cs b/tests/Moq.Tests/VerifyFixture.cs index fca403ad1..0aedf693b 100644 --- a/tests/Moq.Tests/VerifyFixture.cs +++ b/tests/Moq.Tests/VerifyFixture.cs @@ -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; @@ -65,6 +66,22 @@ public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherNotCalle Assert.Contains(@".Execute(It.Is(s => string.IsNullOrEmpty(s)))", mex.Message); } + [Fact] + public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherVariableNotCalled() + { + var mock = new Mock(); + + Expression> nullOrEmpty = s => string.IsNullOrEmpty(s); + + mock.Setup(x => x.Execute(It.Is(nullOrEmpty))) + .Returns("ack") + .Verifiable(); + + var mex = Assert.Throws(() => mock.Verify()); + Assert.True(mex.IsVerificationError); + Assert.Contains(@".Execute(It.Is(s => string.IsNullOrEmpty(s)))", mex.Message); + } + [Fact] public void VerifiesNoOpIfNoVerifiableExpectations() { From cf5af87dff83abe310e060c77563f5523baa9ef6 Mon Sep 17 00:00:00 2001 From: Brian Friesen Date: Mon, 1 Feb 2021 13:32:23 -0500 Subject: [PATCH 2/5] Format lambda expression variables --- src/Moq/StringBuilderExtensions.AppendExpression.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Moq/StringBuilderExtensions.AppendExpression.cs b/src/Moq/StringBuilderExtensions.AppendExpression.cs index 174741e16..51517df8d 100644 --- a/src/Moq/StringBuilderExtensions.AppendExpression.cs +++ b/src/Moq/StringBuilderExtensions.AppendExpression.cs @@ -68,7 +68,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); From 8a2d2ed770e19c4df404cc4ade93b69282ff00b9 Mon Sep 17 00:00:00 2001 From: Brian Friesen Date: Mon, 1 Feb 2021 14:32:04 -0500 Subject: [PATCH 3/5] Add test for closure access --- tests/Moq.Tests/VerifyFixture.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Moq.Tests/VerifyFixture.cs b/tests/Moq.Tests/VerifyFixture.cs index 0aedf693b..783e63f10 100644 --- a/tests/Moq.Tests/VerifyFixture.cs +++ b/tests/Moq.Tests/VerifyFixture.cs @@ -82,6 +82,24 @@ public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherVariable Assert.Contains(@".Execute(It.Is(s => string.IsNullOrEmpty(s)))", mex.Message); } + [Fact] + public void ThrowsWithExpressionIfVerifiableExpectationWithLambdaMatcherVariableAndClosureAccessNotCalled() + { + var mock = new Mock(); + + string password = "abc123"; + + Expression> matchingPassword = s => s == password; + + mock.Setup(x => x.Execute(It.Is(matchingPassword))) + .Returns("ack") + .Verifiable(); + + var mex = Assert.Throws(() => mock.Verify()); + Assert.True(mex.IsVerificationError); + Assert.Contains(@".Execute(It.Is(s => s == password)", mex.Message); + } + [Fact] public void VerifiesNoOpIfNoVerifiableExpectations() { From 5ae449c71a1bd2c168858c3a3ecfc57bb414bfd0 Mon Sep 17 00:00:00 2001 From: Brian Friesen Date: Mon, 1 Feb 2021 14:35:32 -0500 Subject: [PATCH 4/5] Exclude name of the closure class --- src/Moq/StringBuilderExtensions.AppendExpression.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Moq/StringBuilderExtensions.AppendExpression.cs b/src/Moq/StringBuilderExtensions.AppendExpression.cs index 51517df8d..b8d903466 100644 --- a/src/Moq/StringBuilderExtensions.AppendExpression.cs +++ b/src/Moq/StringBuilderExtensions.AppendExpression.cs @@ -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; @@ -259,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 { From e96804f56d49fe8431d4f71fc61931ff3b24a5b3 Mon Sep 17 00:00:00 2001 From: Brian Friesen Date: Tue, 2 Feb 2021 10:54:48 -0500 Subject: [PATCH 5/5] Update the changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1f76c7f..6266ad487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)