devlooped / moq

The most popular and friendly mocking framework for .NET
Other
5.79k stars 798 forks source link

Moq "set" matcher fails when setting StringValues with string #1465

Open mpdelbuono opened 3 months ago

mpdelbuono commented 3 months ago

Describe the Bug

Using the code shown below, we get the following error:

System.ArgumentException : Could not determine the correct positions for all argument matchers (1 in total) used in a call to this method: IHeaderDictionary.set_Location. This could be caused by an unrecognized type conversion, coercion, narrowing, or widening, and is most likely a bug in Moq. Please report your use case to the Moq team.

We suspect the cause must be the coercion from string to StringValues via the implicit conversion operator. When using It.Is<StringValues>() the code works flawlessly.

Steps to Reproduce

The following code reproduces this behavior:

public class ReproTest
{
  [Fact]
  public void Repro()
  {
    Mock<HttpContext> context = new(MockBehavior.Strict);
    context.SetupSet(m => m.Response.Headers.Location = It.Is<string>(s => s.Contains("foo")));
    context.Object.Response.Headers.Location = "https://example.com/foo";
  }
}

Expected Behavior

Not crashing in this way. At a minimum, an error message indicating what was invalid about the matcher. Ideally, it would allow this coercion, since it heavily matches the code under test.

Exception with Stack Trace

System.ArgumentException : Could not determine the correct positions for all argument matchers (1 in total) used in a call to this method: IHeaderDictionary.set_Location. This could be caused by an unrecognized type conversion, coercion, narrowing, or widening, and is most likely a bug in Moq. Please report your use case to the Moq team.

   at Moq.ActionObserver.<ReconstructExpression>g__GetArgumentExpressions|0_0[T](Invocation invocation, Match[] matches) in /_/src/Moq/ActionObserver.cs:line 197
   at Moq.ActionObserver.ReconstructExpression[T](Action`1 action, Object[] ctorArgs) in /_/src/Moq/ActionObserver.cs:line 53
   at Moq.Mock`1.SetupSet(Action`1 setterExpression) in /_/src/Moq/Mock`1.cs:line 711

Version Info

Tested on Moq 4.20.69 and 4.20.70

The test case above uses the following NuGet packages:

Additional Info

kzu commented 2 months ago

Would gladly accept a PR for this enhancement.

Argument types must match exactly, so it's working as designed.

This could be enhanced in that PR by looking at implicit operators, but that adds another reflection lookup and invocation.