我对测试和模拟非常陌生,我正在尝试编写一个测试,以确保我的验证逻辑设置正确的ModelState错误。
我看到的是,controller.ControllerContext.HttpContext.Request是在我第一次检查它时设置的,但是每次检查之后,请求都是空的。
这会在MVC源代码中的*ValueProviderDictionary *类的PopulateDictionary方法中导致一个空引用异常,因为在该方法中请求对象被多次访问,而没有确保请求不是null。
我把我在研究如何克服到目前为止遇到的一些问题时发现的几种技巧和帮助结合在一起,所以现在我有点不确定我可能在哪里介绍了这个问题。
我是不是在这里不正确地使用模拟对象?
考试不及格
//Test
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
//Arrange
DataAccessFactoryMocks.MockAllDaos();
var controller = new FooController();
var testFormCollection = new NameValueCollection();
testFormCollection.Add("foo.CustomerID", "3");
testFormCollection.Add("_fooForm", SerializationUtils.Serialize(new FooModel()));
var mockHttpContext = new MockHttpContext(controller, "POST", testFormCollection, null);
//Accessor used to run the protected OnActionExecuting method in my controller
var accessor = new FooControllerAccessor(controller);
//Request is set, assertion passes
Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.Form);
//Request is null when accessing the property a second time, assertion fails
Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.QueryString);
//Act
accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));
//Assert
Assert.That(controller.ModelState.IsValid == false);
}测试助手
//Test helper to create httpcontext and set controller context accordingly
public class MockHttpContext
{
public HttpContextBase HttpContext { get; private set; }
public HttpRequestBase Request { get; private set; }
public HttpResponseBase Response { get; private set; }
public RouteData RouteData { get; private set; }
public MockHttpContext(Controller onController)
{
//Setup the common context components and their relationships
HttpContext = MockRepository.GenerateMock<HttpContextBase>();
Request = MockRepository.GenerateMock<HttpRequestBase>();
Response = MockRepository.GenerateMock<HttpResponseBase>();
//Setup the context, request, response relationship
HttpContext.Stub(c => c.Request).Return(Request);
HttpContext.Stub(c => c.Response).Return(Response);
Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Request.Stub(r => r.QueryString).Return(new NameValueCollection());
Request.Stub(r => r.Form).Return(new NameValueCollection());
//Apply the context to the suppplied controller
var rc = new RequestContext(HttpContext, new RouteData());
onController.ControllerContext = new ControllerContext(rc, onController);
}
public MockHttpContext(Controller onController, string httpRequestType, NameValueCollection form, NameValueCollection querystring)
{
//Setup the common context components and their relationships
HttpContext = MockRepository.GenerateMock<HttpContextBase>();
Request = MockRepository.GenerateMock<HttpRequestBase>();
Response = MockRepository.GenerateMock<HttpResponseBase>();
//Setup request type based on parameter value
Request.Stub(r => r.RequestType).Return(httpRequestType);
//Setup the context, request, response relationship
HttpContext.Stub(c => c.Request).Return(Request);
HttpContext.Stub(c => c.Response).Return(Response);
Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());
Request.Stub(r => r.QueryString).Return(querystring);
Request.Stub(r => r.Form).Return(form);
//Apply the context to the suppplied controller
var rc = new RequestContext(HttpContext, new RouteData());
onController.ControllerContext = new ControllerContext(rc, onController);
}
}基于 MvcContrib.TestHelper的工作测试
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
//Arrange
DataAccessFactoryMocks.MockAllDaos();
TestControllerBuilder builder = new TestControllerBuilder();
builder.Form.Add("fooModel.CustomerID", "3");
builder.HttpContext.Request.Stub(r => r.RequestType).Return("POST");
FooController controller = builder.CreateController<FooController>();
var accessor = new FooControllerAccessor(controller);
//Act
accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));
//Assert
Assert.IsFalse(controller.ModelState.IsValid);
}发布于 2009-09-14 15:47:38
我建议您使用优秀的MVCContrib TestHelper对Rhino的ASP.NET MVC控制器进行单元测试。您将看到单元测试的急剧简化和可读性的提高。
发布于 2015-09-25 20:02:45
我从您的问题中了解到,对ControllerContext的模拟也可以被存根对象所取代,因为目标不是测试ControllerContext行为。另外,我不太清楚为什么需要FooControllerAccessor,而您唯一关心的是断言ModelState,所以我把它放在这里:
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
// Arrange
var action = new FooController()
.Action("index")
.RequestData(new Dictionary<string, object>()
{
{"foo.CustomerID", 3},
{"_fooForm", new FooModel()}
});
//Act
var modelState = action.ValidateRequest();
//Assert
Assert.That(modelState.IsValid == false);
}要使用这段代码,您应该安装Xania.AspNet.Simulator (在编写v1.4.0-Beta5时)为Mvc4和Mvc5工作
PM > install-package Xania.AspNet.Simulator -Pre
有关更多示例,请参见以下内容:
https://stackoverflow.com/questions/1422347
复制相似问题