Pregunta

Entonces, el contexto del controlador depende de algunos aspectos internos de asp.net.¿Cuáles son algunas formas de simularlos limpiamente para pruebas unitarias?Parece que es muy fácil obstruir las pruebas con toneladas de configuración cuando solo necesito, por ejemplo, Request.HttpMethod para devolver "GET".

He visto algunos ejemplos/ayudantes en las redes, pero algunos están anticuados.Pensé que este sería un buen lugar para guardar lo último y lo mejor.

Estoy usando la última versión de los simulacros de rinoceronte.

¿Fue útil?

Solución

Usando MoQ se parece a esto:

var request = new Mock<HttpRequestBase>();
request.Expect(r => r.HttpMethod).Returns("GET");
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Expect(c => c.Request).Returns(request.Object);
var controllerContext = new ControllerContext(mockHttpContext.Object
, new RouteData(), new Mock<ControllerBase>().Object);

Creo que la sintaxis de Rhino Mocks es similar.

Otros consejos

Aquí hay una clase de prueba unitaria de muestra que utiliza MsTest y Moq que se burla de los objetos HttpRequest y HttpResponse.(.NET 4.0, ASP.NET MVC 3.0)

La acción del controlador obtiene valor de la solicitud y establece el encabezado http en los objetos de respuesta.Otros objetos de contexto http podrían simularse de manera similar

[TestClass]
public class MyControllerTest
{
    protected Mock<HttpContextBase> HttpContextBaseMock;
    protected Mock<HttpRequestBase> HttpRequestMock;
    protected Mock<HttpResponseBase> HttpResponseMock;

    [TestInitialize]
    public void TestInitialize()
    {
        HttpContextBaseMock = new Mock<HttpContextBase>();
        HttpRequestMock = new Mock<HttpRequestBase>();
        HttpResponseMock = new Mock<HttpResponseBase>();
        HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object);
        HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object);
    }

    protected MyController SetupController()
    {
        var routes = new RouteCollection();
        var controller = new MyController();
        controller.ControllerContext = new ControllerContext(HttpContextBaseMock.Object, new RouteData(), controller);
        controller.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, new RouteData()), routes);
        return controller;
    }

    [TestMethod]
    public void IndexTest()
    {
        HttpRequestMock.Setup(x => x["x"]).Returns("1");
        HttpResponseMock.Setup(x => x.AddHeader("name", "value"));

        var controller = SetupController();
        var result = controller.Index();
        Assert.AreEqual("1", result.Content);

        HttpRequestMock.VerifyAll();
        HttpResponseMock.VerifyAll();
    }
}

public class MyController : Controller
{
    public ContentResult Index()
    {
        var x = Request["x"];
        Response.AddHeader("name", "value");
        return Content(x);
    }
}

Aquí hay un fragmento del enlace de Jason.Es lo mismo que el método de Phil pero usa Rhino.

Nota:simuladoHttpContext.Request se bloquea para devolver simulacro antes Las partes internas de mockRequest están apagadas.Creo que este orden es necesario.

// create a fake web context
var mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
var mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockHttpContext.Stub(x => x.Request).Return(mockRequest);

// tell the mock to return "GET" when HttpMethod is called
mockRequest.Stub(x => x.HttpMethod).Return("GET");            

var controller = new AccountController();

// assign the fake context
var context = new ControllerContext(mockHttpContext, 
                  new RouteData(), 
                  controller);
controller.ControllerContext = context;

// act
...

El procedimiento para esto parece haber cambiado ligeramente en MVC2 (estoy usando RC1).La solución de Phil Haack no me funciona si la acción requiere un método específico ([HttpPost], [HttpGet]).Al explorar Reflector, parece que el método para verificar estos atributos ha cambiado.MVC ahora comprueba request.Headers, request.Form, y request.QueryString para X-HTTP-Method-Override valor.

Si agrega simulacros para estas propiedades, funciona:

var request = new Mock<HttpRequestBase>();
request.Setup(r => r.HttpMethod).Returns("POST");
request.Setup(r => r.Headers).Returns(new NameValueCollection());
request.Setup(r => r.Form).Returns(new NameValueCollection());
request.Setup(r => r.QueryString).Returns(new NameValueCollection());

var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Expect(c => c.Request).Returns(request.Object);
var controllerContext = new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object);

O puedes hacer esto con Typemock Isolator sin necesidad de enviar ningún controlador falso:

Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");

He terminado con esta especificación

public abstract class Specification <C> where C: Controller
{
    protected C controller;

    HttpContextBase mockHttpContext;
    HttpRequestBase mockRequest;

    protected Exception ExceptionThrown { get; private set; }

    [SetUp]
    public void Setup()
    {
        mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
        mockRequest = MockRepository.GenerateMock<HttpRequestBase>();

        mockHttpContext.Stub(x => x.Request).Return(mockRequest);
        mockRequest.Stub(x => x.HttpMethod).Return("GET");


        EstablishContext();
        SetHttpContext();

        try
        {
            When();
        }
        catch (Exception exc)
        {
            ExceptionThrown = exc;
        }
    }

    protected void SetHttpContext()
    {
        var context = new ControllerContext(mockHttpContext, new RouteData(), controller);
        controller.ControllerContext = context;
    }

    protected T Mock<T>() where T: class
    {
        return MockRepository.GenerateMock<T>();
    }

    protected abstract void EstablishContext();
    protected abstract void When();

    [TearDown]
    public virtual void TearDown()
    {
    }
} 

y el jugo esta aqui

[TestFixture]
public class When_invoking_ManageUsersControllers_Update :Specification   <ManageUsersController>
{
    private IUserRepository userRepository;
    FormCollection form;

    ActionResult result;
    User retUser;

    protected override void EstablishContext()
    {
        userRepository = Mock<IUserRepository>();
        controller = new ManageUsersController(userRepository);

        retUser = new User();
        userRepository.Expect(x => x.GetById(5)).Return(retUser);
        userRepository.Expect(x => x.Update(retUser));

        form = new FormCollection();
        form["IdUser"] = 5.ToString();
        form["Name"] = 5.ToString();
        form["Surename"] = 5.ToString();
        form["Login"] = 5.ToString();
        form["Password"] = 5.ToString();
    }

    protected override void When()
    {
        result = controller.Edit(5, form);
    }

    [Test]
    public void is_retrieved_before_update_original_user()
    {
        userRepository.AssertWasCalled(x => x.GetById(5));
        userRepository.AssertWasCalled(x => x.Update(retUser));
    }
}

disfrutar

Considero que ese largo procedimiento de burla es demasiada fricción.

La mejor manera que hemos encontrado (usar ASP.NET MVC en un proyecto real) es abstraer HttpContext a una interfaz IWebContext que simplemente pasa.Entonces podrás burlarte del IWebContext sin ningún problema.

Aquí hay un ejemplo

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top