-
-
Notifications
You must be signed in to change notification settings - Fork 798
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
InvalidOperationException when specifiying setup on mock with mock containing property of type Nullable<T> #725
Comments
@dav1dev - Thank you for reporting this. Will look into this ASAP and report findings back here. |
@dav1dev - Nice find! 👍 TL;DR: If you're just interested in fixing this problem, then swap out I'll start with a short recap of what your repro code boils down to, then let's look at what goes wrong: public interface IHandler
{
void Do(Guid id);
void DoNullable(Guid? id);
}
public class DataStructureGuid
{
public virtual Guid? Id { get; }
}
[Fact]
public void Setup_Do_GetUnexpectedException()
{
var id = Guid.NewGuid();
var data = Mock.Of<DataStructureGuid>(x => x.Id == id);
new Mock<IHandler>().Setup(x => x.Do(data.Id.Value));
}
[Fact]
public void Setup_DoNullable_NoException()
{
var id = Guid.NewGuid();
var data = Mock.Of<DataStructureGuid>(x => x.Id == id);
new Mock<IHandler>().Setup(x => x.DoNullable(data.Id));
} The arguments inside the setup expressions ( For each argument in a setup expression, Moq needs to find out whether it is a matcher (such as In order to discover argument matchers, Moq makes use of an internal component called the "ambient observer". This is a special mode of operation that, when activated on the current thread, will take note of all mock invocations and matcher invocations. Moq will activate the ambient observer, then compile and execute the argument expression, and check with the observer whether a matcher was invoked. (See these lines of code.) One side effect of the ambient observer is that setups of invoked mocks will be ignored (see these lines of code). This is very much necessary for other use cases of the ambient observer. Being an "observer", it shouldn't cause state changes of its own, thus setups which can have Now, in all of your repro unit tests, So when Moq tries to discover whether A fix for this problem will be tricky, but would seem feasible. |
@stakx Thank you for this insight. A fix for this case did not pop into my mind just yet. Meanwhile I worked around the issue by specifying the setups with an extra variable as demonstrated below: var id = Guid.NewGuid();
var data = Mock.Of<DataStructureGuid>(x => x.Id == id);
_handler.Setup(x => x.Do(id)).Returns(Mock.Of<IResult>());
_testee.Process(data); |
@dav1dev - Glad you found a workaround for the time being! Regarding a bug fix, there are several options. My personal favourite would be to bring back the There are other options (making the matcher recognition algorithm more intelligent by inspecting the argument expressions more closely before executing them) but that gets complicated very quickly. |
@stakx Thank you for closing this issue! |
Description
Value of nullable object gets lost while it is used to setup a method call on another mock. This results in an InvalidOperationException.
Example:
myMock.Setup(x => x.Do(dataMock.Id.Value));
Expected Behavior
Value of nullable type remains on mock as setup.
Actual Behavior
Mock instance/context gets lost in scope of setup call on another mock in case the referenced property is of nullable type.
Reproduction
To reproduce this issue I created a repository: https://github.com/dav1dev/moq-NullableIssue.
The issue is reliably reproduced in failing tests in class ProcessorTest.
Versions
The issue can be reproduced in moq version 4.10.0 on .NET framework 4.6.1. Castle.Core is used in version 4.3.1.
The text was updated successfully, but these errors were encountered: