Контролируемый контекст контроллера Asp.net-mvc

Таким образом, контекст контроллера зависит от некоторых внутренних компонентов asp.net. Каковы некоторые способы их чистого макета для модульных тестов? Похоже, что очень легко засорять тесты множеством настроек, когда мне нужно только, например, Request.HttpMethod для возврата "GET".

Я видел несколько примеров / помощников по сетям, но некоторые датированы. Я подумал, что это было бы хорошим местом, чтобы сохранить последние и лучшие.

Я использую последнюю версию носорогов издевается

7 ответов

Решение

С использованием MoQ это выглядит примерно так:

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

Я думаю, что синтаксис Rhino Mocks похож.

Ниже приведен пример класса модульного тестирования с использованием MsTest и Moq, который выполняет макет объектов HttpRequest и HttpResponse. (.NET 4.0, ASP.NET MVC 3.0)

Действие контроллера получает значение из запроса и устанавливает заголовок http в объектах ответа. Другие объекты контекста http могут быть смоделированы подобным образом

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

Вот фрагмент ссылки Джейсона. Это так же, как метод Фила, но использует носорога.

Примечание: mockHttpContext.Request заглушается, чтобы вернуть mockRequest до того, как внутренности mockRequest будут заглушены. Я считаю, что этот заказ требуется.

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

Процедура для этого, кажется, немного изменилась в MVC2 (я использую RC1). Решение Фила Хаака не работает для меня, если действие требует определенного метода ([HttpPost], [HttpGet]). Разобравшись в Reflector, похоже, что метод проверки этих атрибутов изменился. MVC теперь проверяет request.Headers, request.Form, а также request.QueryString для X-HTTP-Method-Override значение.

Если вы добавляете макеты для этих свойств, это работает:

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

Или вы можете сделать это с Typemock Isolator без необходимости отправлять поддельный контроллер вообще:

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

Я закончил с этой спецификацией

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

и сок здесь

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

наслаждаться

Я считаю, что эта длинная насмешливая процедура - слишком много трений.

Лучший способ, который мы нашли - использование ASP.NET MVC в реальном проекте - это абстрагирование HttpContext в интерфейс IWebContext, который просто проходит через него. Тогда вы можете издеваться над IWebContext без боли.

Вот пример

Другие вопросы по тегам