Domanda

Sono abbastanza nuovo nel testare e deridere e sto provando a scrivere un test che assicurerà che la mia logica di validazione stia impostando correttamente gli errori ModelState.

Quello che vedo è che il controller.ControllerContext.HttpContext.Request è impostato la prima volta che lo controllo, ma ogni volta che Richiesta è null.

Ciò sta causando un'eccezione di riferimento null nel metodo PopulateDictionary della classe * ValueProviderDictionary * nell'origine MVC perché all'oggetto richiesta si accede più volte in quel metodo senza assicurarsi che la richiesta non sia nulla.

Sto unendo diverse tecniche e aiutanti che ho trovato mentre cercavo come superare alcuni dei problemi che ho incontrato finora, quindi a questo punto non sono sicuro di dove potrei aver introdotto il problema.

Sto usando oggetti finti in modo errato qui?

Test fallito

//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

//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);
    }
}

Test di funzionamento utilizzando 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);
    }
È stato utile?

Soluzione

Ti suggerirei di utilizzare l'eccellente MVCContrib TestHelper per l'unità testare i controller ASP.NET MVC con Rhino Mocks. Vedrai una drastica semplificazione dei test unitari e una maggiore leggibilità.

Altri suggerimenti

Quello che ho capito dalla tua domanda è che il deridere su ControllerContext potrebbe anche essere sostituito da un oggetto stub perché l'obiettivo non sta testando il comportamento di ControllerContext. Inoltre, non sono davvero sicuro del motivo per cui hai bisogno di un FooControllerAccessor mentre la tua unica preoccupazione è affermare ModelState, quindi l'ho lasciato fuori qui:

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);
}

Per utilizzare questo codice è necessario installare Xania.AspNet.Simulator (al momento della stesura di v1.4.0-beta5) funziona per Mvc4 e Mvc5

  

PM > pacchetto di installazione Xania.AspNet.Simulator -Pre

Per ulteriori esempi, controlla quanto segue:

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top