It took me some time to figure out how to do this. The documentation on the MoQ quickstart wiki says to use the following pattern:
// expects an invocation to set the value to "foo"
mock.SetupSet(foo => foo.Name = "foo");
Well, I tried this with an object:
public class SomeClass {
public string Something { get; set; }
}
[Test]
public void PropertySetupOnMock() {
const string someString = "Should be set";
var mocker = new Mock<SomeClass>();
mocker.SetupSet(x => x.Something = someString); //exception here
mocker.Object.Something = someString;
mocker.VerifySet(x => x.Something);
}
This only gave me an error on the line with the
SetupSet
:System.ArgumentException: Invalid expectation on a non-overridable member.
Well, that's strange. After some looking around it turns out that MoQ does its magic by extending the class that's being mocked. It cannot extend non-virtual properties, so this
Something
property cannot be extended and thus cannot be Setup.What can be done about this? The easiest immediate solution would be to mark the property
virtual
, like so:
public class SomeClass {
public virtual string Something { get; set; }
}
But do we really want to decorate every property on the objects as virtual? Not really. A better solution is to push the property up to an interface and mock that interface instead, like so:
public interface IWithProperty {
public string Something { get; set; }
}
public class SomeClass : IWithProperty {
public string Something { get; set; }
}
[Test]
public void PropertySetupOnInterfaceMock() {
const string someString = "Should be set";
var mocker = new Mock<IWithProperty>();
mocker.SetupSet(x => x.Something = someString); //no exception here
mocker.Object.Something = someString;
mocker.VerifySet(x => x.Something);
}
Thus allowing me to mock it out and test that the property setter was indeed called. Yay!
If anyone has any better ways of doing this I'd love to hear from you!
2 comments:
Interfaces are totally the way to go, but there are some situations where you simply can't mock your way out of it. If you try to mock out a Control object (or any non-interfaces class in the .NET framework), you're kind of up a creek.
Unless, of course, you were to write an interface-based abstraction layer to talk to whatever .net thing you were interacting with, but that seems a bit much.
Thanks for commenting!
I agree, interfaces are the way to go whenever you can. Sometimes you cannot, though. In particular when writing tests against legacy code or when some other restriction is put on the hierarchy.
Post a Comment