Domanda

Quindi il contesto del controller dipende da alcuni interni di asp.net.Quali sono alcuni modi per simularli in modo pulito per i test unitari?Sembra che sia molto facile intasare i test con tonnellate di configurazione quando ho solo bisogno, ad esempio, di Request.HttpMethod per restituire "GET".

Ho visto alcuni esempi/aiutanti in rete, ma alcuni sono datati.Ho pensato che questo sarebbe stato un buon posto per tenere le ultime novità.

Sto usando l'ultima versione dei mock di rinoceronte

È stato utile?

Soluzione

Usando MoQ assomiglia a questo:

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

Penso che la sintassi di Rhino Mocks sia simile.

Altri suggerimenti

Di seguito è riportato un esempio di classe di test unitario che utilizza MsTest e Moq che simula gli oggetti HttpRequest e HttpResponse.(.NET 4.0, ASP.NET MVC 3.0)

L'azione del controller ottiene valore dalla richiesta e imposta l'intestazione http negli oggetti di risposta.Altri oggetti di contesto http potrebbero essere presi in giro in modo simile

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

Ecco uno snippet dal collegamento di Jason.È lo stesso del metodo di Phil ma usa il rinoceronte.

Nota:mockHttpContext.Request viene sottoposto a stub per restituire mockRequest Prima I componenti interni di mockRequest vengono disattivati.Credo che questo ordine sia necessario.

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

La procedura sembra essere leggermente cambiata in MVC2 (sto usando RC1).La soluzione di Phil Haack non funziona per me se l'azione richiede un metodo specifico ([HttpPost], [HttpGet]).Esplorando Reflector, sembra che il metodo per verificare questi attributi sia cambiato.MVC ora controlla request.Headers, request.Form, E request.QueryString per un X-HTTP-Method-Override valore.

Se aggiungi mock per queste proprietà, funziona:

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

Oppure puoi farlo con Typemock Isolator senza la necessità di inviare un controller falso:

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

ho finito con queste specifiche

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()
    {
    }
} 

e il succo è qui

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

Godere

Trovo che quella lunga procedura beffarda sia troppo attrito.

Il modo migliore che abbiamo trovato, utilizzando ASP.NET MVC su un progetto reale, consiste nell'astrarre HttpContext in un'interfaccia IWebContext che passa semplicemente.Quindi puoi deridere IWebContext senza problemi.

Ecco un esempio

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