Why should the property I want to make fun of be virtual?

I am doing some unit tests and mocking some properties using Moq .

Now this is the Test Controller (ASP.NET MVC 3). My controllers come from an abstract controller called AbstractController .

This controller is dependent on the Http Context (in order to do such things as topics, domain-specific logic, based on HTTP HOST headers, etc.).

This is done using the WebSiteSettings property:

public abstract class AbstractController : Controller { public WebSiteSettings WebSiteSettings { get; private set; } // other code } 

Pay attention to a private set - ctor installs it. So, I changed it to use an interface, and what I made fun of:

 public IWebSiteSettings WebSiteSettings { get; private set; } 

Then I created a “FakeWebSiteSettings” that mocks the Http content to read the HTTP headers.

The problem is that when I run the test, I get a NotSupportedException:

Incorrect setting for a non-virtual (redefined in VB) member: x => x.WebSiteSettings

Here is the corresponding 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 create the WebsiteSettings virtual property, the test passes.

But I can’t understand why I need it. I do not redefine the property, I just mock how it is configured.

Am I missing something or did it wrong?

+35
c # unit-testing asp.net-mvc moq controller
Apr 14 2018-11-11T00:
source share
4 answers

Moq and other similar fake frameworks can only simulate interfaces, abstract methods / properties (according to abstract classes) or virtual methods / properties on specific classes.

This is because it creates a proxy server that implements the interface or creates a derived class that overrides these overridable methods to intercept calls.

+53
Apr 14 2018-11-11T00:
source share

I created an interface and a wrapper class. eg.

  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 they simply mock the interface:

  var mockWebClient = new Mock<IWebClient>(); 

Obviously, you may need to include more properties / methods. But the trick.

Another useful trick for other ridicule, such as changing the current time (I always use UTC date time):

 public interface IDateTimeUtcNowProvider { DateTime UtcNow { get; } } public class DateTimeUtcNowProvider : IDateTimeUtcNowProvider { public DateTime UtcNow { get { return DateTime.UtcNow; } } } 

eg. if you have a service that starts every x minutes, you can simply mock IDateTimeProvider and return a time that later checks that the service is starting up again ... or something else.

+4
Jun 10 '13 at 0:52
source share

"So ... what have I done is the only way?"

No, not the only way - you are much better off implementing the interface and taunting it. Then your actual methods may be virtual or not of your choice.

+3
Aug 26 '12 at 23:57
source share

Despite the fact that everything that was said earlier is true, it is worth knowing that the proxy-bullying approach (for example, the one that uses moq) is not the only one.

Check http://www.typemock.com/ for a comprehensive solution that allows you to mock private classes, not virtual methods, etc. Pretty powerful.

0
Dec 27 '12 at 15:44
source share



All Articles