Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the property I want to mock need to be virtual?

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?

like image 859
RPM1984 Avatar asked Apr 14 '11 23:04

RPM1984


People also ask

How do you set a mock object property?

moqUser. SetupAllProperties(); This method will prepare all properties on the mock to be able to record the assigned value, and replay it later (i.e. to act as real property). You can also use SetupProperty() method to set up individual properties to be able to record the passed in value.

Why do we need mock objects?

Using mock objects allows developers to focus their tests on the behavior of the system under test without worrying about its dependencies. For example, testing a complex algorithm based on multiple objects being in particular states can be clearly expressed using mock objects in place of real objects.

What is the point of mock testing?

Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.


2 Answers

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.

like image 152
aqwert Avatar answered Oct 05 '22 21:10

aqwert


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.

like image 21
mkaj Avatar answered Oct 05 '22 21:10

mkaj