Подтвердить что ты не робот

Модуль тестирования контроллера в ASP.NET MVC 2 с RedirectToAction

У меня есть контроллер, который реализует простую операцию Add на сущности и перенаправляет на страницу Details:

[HttpPost]
public ActionResult Add(Thing thing)
{ 
    // ... do validation, db stuff ...
    return this.RedirectToAction<c => c.Details(thing.Id));
}

Это отлично работает (используя RedirectToAction из сборки MvcContrib).

Когда я тестирую этот метод, я хочу получить доступ к представлению ViewData, которое возвращается из действия Details (так что я могу получить только что добавленный первичный ключ и доказать, что он теперь находится в базе данных).

Тест имеет:

var result = controller.Add(thing);

Но результат здесь имеет тип: System.Web.Mvc.RedirectToRouteResult (который является System.Web.Mvc.ActionResult). Он еще не выполнил метод Details.

Я пробовал называть ExecuteResult на возвращенном объекте, проходящем в mocked up ControllerContext, но структура была недовольна отсутствием деталей в издеваемом объекте.

Я мог бы попробовать заполнить детали и т.д., но тогда мой тестовый код намного длиннее кода, который я тестирую, и я чувствую, что мне нужны модульные тесты для модульных тестов!

Я что-то упустил в философии тестирования? Как проверить это действие, когда я не могу получить его возвращенное состояние?

4b9b3361

Ответ 1

Я использую MVC2 RC2 на данный момент, и ответ от rmacfie не совсем сработал у меня, но сделал меня на правильном пути.

Правильно или неправильно я смог сделать это в своем тесте:

var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model);

actionResult.RouteValues["action"].should_be_equal_to("Index");
actionResult.RouteValues["controller"].should_be_equal_to("Logon");

Не уверен, что это поможет кому-то, но может сэкономить вам 10 минут.

Ответ 2

Есть MVC Contrib TestHelper, которые являются фантастическими для тестирования большей части ActionResult

Вы можете получить его здесь: http://mvccontrib.codeplex.com/wikipage?title=TestHelper

Вот пример синтаксиса:

var controller = new TestController();

controller.Add(thing)
          .AssertActionRedirect()
          .ToAction<TestController>(x => x.Index());

Чтобы проверить, были ли данные сохранены успешно, вы можете, возможно, напрямую обратиться к своей базе данных, я не знаю, используете ли вы ORM или что-то в этом роде, но вы должны сделать что-то, чтобы получить последний элемент, который вы указали в своей базе данных, затем сравните со значением, которое вы указали в Add ActionResult, и посмотрите, нормально ли это.

Я не думаю, что проверка ваших деталей. ActionResult для проверки того, сохраняются ли ваши данные, является правильным подходом. Это не будет unit test, более функциональный тест.

Но вы также должны unit test ваш метод Details, чтобы убедиться, что ваш объект viewdata заполнен нужными данными, поступающими из вашей базы данных.

Ответ 3

Вы, кажется, слишком много делаете для unit test. Проверка и доступ к данным обычно выполняются службами, которые вы вызываете из действия контроллера. Вы издеваетесь над этими услугами и проверяете только то, что они были вызваны должным образом.

Что-то вроде этого (с использованием приблизительного синтаксиса для Rhino.Mocks и NUnit):

[Test]
public void Add_SavesThingToDB()
{
    var dbMock = MockRepository.GenerateMock<DBService>();
    dbMock.Expect(x => x.Save(thing)).Repeat.Once();

    var controller = new MyController(dbMock);
    controller.Add(new Thing());

    dbMock.VerifyAllExpectations();
}

[Test]
public void Add_RedirectsAfterSave()
{
    var dbMock = MockRepository.GenerateMock<DBService>();

    var controller = new MyController(dbMock);
    var result = (RedirectToRouteResult)controller.Add(new Thing());

    Assert.That(result.Url, Is.EqualTo("/mynew/url"));
}

Ответ 4

У меня есть статический вспомогательный метод, который проверяет перенаправление.

public static class UnitTestHelpers
{
    public static void ShouldEqual<T>(this T actualValue, T expectedValue)
    {
        Assert.AreEqual(expectedValue, actualValue);
    }

    public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues)
    {
        RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues;
        var expectedValues = new RouteValueDictionary(expectedRouteValues);

        foreach (string key in expectedValues.Keys)
        {
            Assert.AreEqual(expectedValues[key], actualValues[key]);
        }
    }
}

Тогда создание теста перенаправления очень просто.

[Test]
public void ResirectionTest()
{
    var result = controller.Action();

    result.ShouldBeRedirectionTo(
        new
        {
            controller = "ControllerName",
            action = "Index"
        }
    );
}