2008-08-28 18 views
62

So hängt der Controller-Kontext von einigen asp.net Interna. Was sind einige Möglichkeiten, diese für Unit-Tests sauber nachzubilden? Scheint wie es sehr einfach ist, Tests mit Tonnen von Setup zu verstopfen, wenn ich nur Request.HttpMethod brauche, um "GET" zurückzugeben.Mocking Asp.net-mvc Controller-Kontext

Ich habe einige Beispiele/Helfer in den Netzen gesehen, aber einige sind veraltet. Dies wäre ein guter Ort, um die neuesten und besten zu halten.

ich neueste Version von rhino bin mit spottet

+0

Ich dachte darüber nach, dies zu tun. Aber nur der Schein für die Datenbankverbindung benötigt. Anstatt die Datenbankzuordnung zu testen, habe ich die Funktion in eine reguläre Klasse verschoben und nur diese Funktion ohne Datenbankverbindung getestet. – MrFox

Antwort

58

Mit MoQ es so etwas wie folgt aussieht:

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

Ich denke, die Rhino Mocks Syntax ähnlich ist.

+16

Dies ist in MVC3 nicht mehr gültig. Wenn Sie eine leere RouteData übergeben, werden Ausnahmen ausgelöst, wenn die nicht virtuelle, nicht vorstellbare Methode GetRequiredString für RouteData aufgerufen wird. – ScottKoon

+2

@ScottKoon Bitte stellen Sie eine Demo zur Verfügung, wie es aussehen sollte – Jon

+1

Die Antwort auf diese Frage zeigt Ihnen, wie Sie die RouteData verspotten. http://stackoverflow.com/questions/986183/mocking-the-routedata-class-in-system-web-routing-for-mvc-applications – ScottKoon

1

Ich finde, dass lange Spott Verfahren zu viel Reibung.

Der beste Weg, den wir gefunden haben - ASP.NET MVC in einem realen Projekt verwenden - besteht darin, den HttpContext zu einer IWebContext-Schnittstelle zu abstrahieren, die einfach durchläuft. Dann können Sie den IWebContext ohne Schmerzen verspotten.

Hier ist ein example

+1

Können Sie das ein bisschen mehr erklären? – user40097

17

Hier ist ein Ausschnitt aus Jasons Link. Es ist das gleiche wie Phils Methode, verwendet aber Rhino.

Hinweis: mockHttpContext.Request ist stubbed mockRequest vor mockRequest Interna zurückkehren werden gerodet werden. Ich glaube, dass diese Reihenfolge erforderlich ist.

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

Ich habe mit dieser Spezifikation fertig

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

und der Saft ist hier

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

genießen

7

Oder Sie können dies ohne die Notwendigkeit, mit Typemock Trenner tun einen falschen Controller überhaupt zu senden:

9

Der Vorgang scheint sich in MVC2 leicht geändert zu haben (ich benutze RC1). Phil Haacks Lösung funktioniert bei mir nicht, wenn die Aktion eine bestimmte Methode erfordert ([HttpPost], [HttpGet]). Wenn Sie in Reflector herumstöbern, sieht es so aus, als ob sich die Methode zum Überprüfen dieser Attribute geändert hat. MVC überprüft jetzt request.Headers, request.Form und request.QueryString für einen X-HTTP-Method-Override Wert.

Wenn Sie Mocks für diese Eigenschaften hinzufügen, funktioniert es:

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

Dies funktionierte für mich, aber in MVC2 RC musste ich auch folgendes hinzufügen: request.Setup (r => r.Files) .Returns (neue Mock (). Object); sonst bekomme ich eine nullreferenceexception –

19

Hier ist ein Beispiel Unit-Test-Klasse MSTest und Moq mit der Httprequest und Httpresponse Objekte verspottet. (.NET 4.0, ASP.NET MVC 3.0)

Controlleraktion erhält Wert von Anforderung und legt HTTP-Header in Antwortobjekten fest.Andere http-Kontextobjekte könnten in ähnlicher Weise aufgespottet werden.

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

Vielen, vielen Dank! Es hat mir sehr geholfen. Ich wünschte, ich könnte es zehnmal verbessern. –

+0

@Maksym Kozlenko Vielen, vielen Dank. Das hat mir sehr geholfen –