Проблемы с издевательством над запросом MVC ControllerContext
-
07-07-2019 - |
Вопрос
Я довольно новичок в тестировании и макетировании, и я пытаюсь написать тест, который гарантирует, что моя логика проверки правильно устанавливает ошибки ModelState.
Что я вижу, так это то, что контроллер.ControllerContext.HttpContext.Request устанавливается при первой проверке, но каждый раз после этого Запрос равно нулю.
Это вызывает исключение нулевой ссылки в Населенный пункт метод класса *ValueProviderDictionary * в исходном коде MVC, потому что к объекту запроса обращаются несколько раз в этом методе, не гарантируя, что запрос не равен нулю.
Я склеиваю несколько методов и помощников, которые я нашел, исследуя, как преодолеть некоторые из проблем, с которыми я столкнулся до сих пор, поэтому на данный момент я немного не уверен, где я, возможно, ввел проблему.
Я неправильно использую здесь фиктивные объекты?
Неудачный тест
//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);
}
Решение
Я бы предложил вам использовать превосходный MVCContrib TestHelper для устройства тестирование ваших контроллеров ASP.NET MVC с помощью Rhino Mocks. Вы увидите резкое упрощение модульных тестов и повышение читабельности.
Другие советы
Что я понимаю из вашего вопроса, так это то, что насмешка над 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
Премьер -министр > установить-пакет Xania.AspNet.Simulator -Предварительный
Для получения дополнительных примеров ознакомьтесь со следующим: