我正在进行一些单元测试,并使用 Moq 模拟一些属性。
现在,这是一个 Controller 测试(ASP.NET MVC 3)。我的控制器派生自抽象控制器,称为 AbstractController 。
此控制器依赖于Http Context(为了执行主题,基于HTTP HOST头的特定于域的逻辑等)。
这是通过名为 WebSiteSettings :
的属性完成的public abstract class AbstractController : Controller
{
public WebSiteSettings WebSiteSettings { get; private set; }
// other code
}
注意私人集 - ctor设置它。所以,我把它更改为使用了一个界面,这就是我所嘲笑的:
public IWebSiteSettings WebSiteSettings { get; private set; }
然后我创建了一个“FakeWebSiteSettings”,它嘲笑Http Context以便它读取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);
如果我使WebsiteSettings
属性虚拟 - 测试通过。
但我无法理解为什么我需要这样做。我实际上并没有覆盖属性,我只是在嘲笑它是如何设置的。
我错过了什么,或者做错了吗?
答案 0 :(得分:58)
Moq和其他类似的模拟框架只能模拟接口,抽象方法/属性(在抽象类上)或具体类上的虚方法/属性。
这是因为它生成了一个代理,它将实现接口或创建一个派生类,该类覆盖那些可重写的方法以拦截调用。
答案 1 :(得分:4)
我已经创建了一个接口和包装类。 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>();
显然,您可能需要包含更多属性/方法。但是诀窍。
其他模拟问题的另一个有用技巧,例如修改当前日期时间(我总是使用UTC日期时间):
public interface IDateTimeUtcNowProvider
{
DateTime UtcNow { get; }
}
public class DateTimeUtcNowProvider : IDateTimeUtcNowProvider
{
public DateTime UtcNow { get { return DateTime.UtcNow; } }
}
e.g。如果你有一个每x分钟运行一次的服务,你可以模拟出IDateTimeProvider并返回一个稍后检查服务再次运行的时间......或者其他什么。
答案 2 :(得分:3)
“所以......我所做的只是唯一的方式?”
不是唯一的方法 - 你最好实现一个界面并嘲笑它。那么您的实际方法可以是虚拟的,也可以不是您选择的。
答案 3 :(得分:0)
虽然之前说的都是真的,但值得知道代理模拟方法(就像moq使用的方法)我不是唯一可能的方法。
检查http://www.typemock.com/是否有全面的解决方案,允许您模拟密封类,非虚拟方法等。非常强大。