C# 为什么我要模拟的属性需要是虚拟的?

C# 为什么我要模拟的属性需要是虚拟的?,c#,asp.net-mvc,unit-testing,controller,moq,C#,Asp.net Mvc,Unit Testing,Controller,Moq,我正在做一些单元测试,并使用Moq模拟一些属性 现在,这是一个控制器测试(ASP.NET MVC 3)。我的控制器源于一个抽象控制器,称为AbstractController 此控制器依赖于Http上下文(以便进行主题化、基于Http主机头的特定于域的逻辑等操作) 这是通过名为网站设置的属性完成的: public abstract class AbstractController : Controller { public WebSiteSettings WebSiteSettings {

我正在做一些单元测试,并使用Moq模拟一些属性

现在,这是一个控制器测试(ASP.NET MVC 3)。我的控制器源于一个抽象控制器,称为AbstractController

此控制器依赖于Http上下文(以便进行主题化、基于Http主机头的特定于域的逻辑等操作)

这是通过名为网站设置的属性完成的:

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

   // other code
}
请注意私有集-由ctor设置。因此,我将其更改为使用接口,这就是我所嘲笑的:

public IWebSiteSettings WebSiteSettings { get; private set; }
然后我创建了一个“FakeWebSiteSettings”,它模拟Http上下文,以便它读取Http头

问题是,当我运行测试时,我得到一个NotSupportedException:

非虚拟(在VB中可重写)成员上的设置无效:x=>x.WebSiteSettings

以下是相关的模拟代码:

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);
var mockWebSiteSettings=new Mock();
var mockController=new Mock(SomeRepository);
mockController.Setup(x=>x.WebSiteSettings).Returns(mockWebSiteSettings.Object);
_controller=mockController.Object;
var httpContextBase=mvcmockhelopers.FakeHttpContext();
httpContextBase.Setup(x=>x.Request.ServerVariables)。返回(新的NameValueCollection
{
{“HTTP_HOST”,“localhost.www.mydomain.com”},
});
_controller.SetFakeControllerContext(httpContextBase.Object);
如果我将
网站设置
属性设置为虚拟,则测试通过

但我不明白为什么我要这么做。我实际上并没有重写该属性,我只是在模拟它的设置方式


我是遗漏了什么,还是做错了?

Moq和其他类似的模拟框架只能模拟接口、抽象方法/属性(在抽象类上)或虚拟方法/属性(在具体类上)

这是因为它生成了一个代理,该代理将实现接口或创建一个派生类,该派生类重写那些可重写的方法以拦截调用。

“那么……我所做的是唯一的方法吗?”


不,这不是唯一的方法——实现一个接口并模仿它会更好。那么,您的实际方法可以是虚拟的,也可以不是您选择的。

尽管前面所说的都是真的,但值得一提的是,代理模拟方法(如moq使用的方法)并不是唯一可能的方法


检查一个全面的解决方案,它允许您模拟密封类、非虚拟方法等。非常强大。

我创建了一个接口和包装类。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);
        }
    }
然后在单元测试中模拟接口:

        var mockWebClient = new Mock<IWebClient>();

e、 g.如果您有一个每x分钟运行一次的服务,您可以模拟IDateTimeProvider,并返回一个稍后的时间来检查该服务是否再次运行。。。或者随便什么。

最低起订量的唯一方法。其他如TypeMock隔离器、Moles可以拦截这些方法/属性我对这个答案感兴趣,但不理解!Giles,你能用RPM1984的原始示例详细说明一下吗?对于.NET5
ISystemClock
public interface IDateTimeUtcNowProvider
{
    DateTime UtcNow { get; } 
}

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