I'm doing some unit testing, and mocking some properties using Moq.
Now, this is a Controller test (ASP.NET MVC 3). My Controllers derive from an abstract controller, called AbstractController.
This controller has a dependency on the Http Context (in order to do things like theming, domain-specific logic based on HTTP HOST headers, etc).
This is done via a property called WebSiteSettings:
public abstract class AbstractController : Controller
{
public WebSiteSettings WebSiteSettings { get; private set; }
// other code
}
Notice the private set - the ctor sets it up. So, i changed it to used an interface, and that's what i've mocked:
public IWebSiteSettings WebSiteSettings { get; private set; }
I then created a "FakeWebSiteSettings", which mocks the Http Context in order for it to read the HTTP headers.
The problem is, when i run the test, i get a NotSupportedException:
Invalid setup on a non-virtual (overridable in VB) member: x => x.WebSiteSettings
Here's the relevant mocking code:
var mockWebSiteSettings = new Mock<FakeWebSiteSettings>();
var mockController = new Mock<MyController>(SomeRepository);
mockController.Setup(x => x.WebSiteSettings).Returns(mockWebSiteSettings.Object);
_controller = mockController.Object;
var httpContextBase = MvcMockHelpers.FakeHttpContext();
httpContextBase.Setup(x => x.Request.ServerVariables).Returns(new NameValueCollection
{
{"HTTP_HOST","localhost.www.mydomain.com"},
});
_controller.SetFakeControllerContext(httpContextBase.Object);
If i make the WebsiteSettings
property virtual - the test passes.
But i can't understand why i need to do this. I'm not actually overriding the property, i'm simply mocking how it is setup.
Am i missing something, or doing this wrong?
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.
I've created an interface and wrapper class. e.g.
public interface IWebClient
{
string DownloadString(string url);
}
public class WebClient : IWebClient
{
private readonly System.Net.WebClient _webClient = new System.Net.WebClient();
public string DownloadString(string url)
{
return _webClient.DownloadString(url);
}
}
and then in your unit tests just mock out the interface:
var mockWebClient = new Mock<IWebClient>();
Obviously you may need to include more properties / methods. But does the trick.
Another useful trick for other mocking problems, such as modifying the current date time (I always use UTC date time):
public interface IDateTimeUtcNowProvider
{
DateTime UtcNow { get; }
}
public class DateTimeUtcNowProvider : IDateTimeUtcNowProvider
{
public DateTime UtcNow { get { return DateTime.UtcNow; } }
}
e.g. if you have a service that runs every x minutes you can just mock out the IDateTimeProvider and return a time that is later to check the service ran again... or whatever.
"So....what i did is the only way?"
No not the only way - you are much better off implementing an interface and mocking that. Then your actual methods can be virtual or not as you choose.
Although, all that was said before is true, it's worth to know that the proxy-mocking approach (like the one that moq uses)i s not the only one possible.
Check http://www.typemock.com/ for a comprehensive, solution, that allows you to mock both sealed classes, non-virtual methods etc. Pretty powerful.