Модульное тестирование контроллера в ASP.NET MVC 2 с RedirectToAction - PullRequest
20 голосов
/ 14 марта 2010

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

[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 для возвращаемого объекта, передаваемого в макете ControllerContext, но фреймворк не был доволен отсутствием детализации в макете объекта.

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

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

Ответы [ 4 ]

31 голосов
/ 17 марта 2010

В данный момент я использую 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 минут.

12 голосов
/ 15 марта 2010

Существует 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 на предмет сохранения ваших данных - это правильный подход. Это был бы не модульный тест, а скорее функциональный тест.

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

7 голосов
/ 14 марта 2010

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

Примерно так (используя приблизительный синтаксис для 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"));
}
6 голосов
/ 22 октября 2010

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

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"
        }
    );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...