Reputation: 1273
I'm moqing an interface which has:
Dictionary<string, object> InstanceVariables { get; set; }
I've created a new mock of the interface and trying to set it up so it returns just a random string like so:
_mockContext.SetupGet(m => m.InstanceVariables[It.IsAny<string>()]).Returns(@"c:\users\randomplace");
But I seem to get an error:
{"Invalid setup on a non-virtual (overridable in VB) member: m => m.InstanceVariables[It.IsAny<String>()]"}
What does this mean exactly? I am mocking the interface so shouldn't this not be a problem?
Thanks
Upvotes: 1
Views: 18789
Reputation: 18649
There's a number of reasons I'd advise against it. Firstly, as mentioned in the comments there's no reason why a real dictionary wouldn't work here. We shouldn't mock types that what we don't own and only mock types we do own.
The line _mockContext.SetupGet(m => m.InstanceVariables[It.IsAny<string>()]).Returns(@"c:\users\randomplace");
is trying to mock the Get
on Dictionary<T, T>
which as @RB notes isn't virtual
, hence your error. You may be mocking your interface, but the set up there is on the .NET dictionary.
Secondly, IMO, It.IsAny<string>()
leads to quite weak tests, because it will respond to, well, any string. Structuring something like:
const string MyKey = "someKey";
var dictionary = new Dictionary<string, object>();
dictionary.Add(MyKey, @"c:\users\randomplace");
_mockContext.Setup(m => m.InstanceVariables).Returns(dictionary);
var sut = new SomeObject(_mockContext.Object());
var result = sut.Act(MyKey);
// Verify
will be stronger as the dictionary can only respond with the path if the correct key is given to / or generated by your System Under Test (sut).
That said, if you absolutely must mock a dictionary, for reasons that aren't apparent on the question... then the property on your interface needs to be the interface for the dictionary, IDictionary
, not the concrete class:
IDictionary<string, object> InstanceVariables { get; set; }
Then you can create the dictionary mock via:
var dictionary = new Mock<IDictionary<string, object>>();
dictionary.SetupGet(d => d[It.IsAny<string>()]).Returns(@"c:\users\randomplace");
Then on the context mock:
_mockContext.SetupGet(d => d.InstanceVariables).Returns(dictionary.Object);
Why does it need to be Virtual?
Moq and other similar mocking frameworks can only mock interfaces, abstract methods/properties (on abstract classes) or virtual methods/properties on concrete classes.
This is because it generates a proxy that will implement the interface or create a derived class that overrides those overrideable methods in order to intercept calls.
Credit to @aqwert
Upvotes: 7