使用RedirectToAction对ASP.NET MVC 2中的控制器进行单元测试

时间:2010-03-13 22:00:36

标签: asp.net-mvc unit-testing

我有一个控制器,它在一个实体上实现一个简单的Add操作,并重定向到Details页面:

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

这很好用(使用MvcContrib程序集中的RedirectToAction)。

当我单元测试此方法时,我想访问从Details操作返回的ViewData(因此我可以获取新插入的东西的主键并证明它现在在数据库中)。

测试有:

var result = controller.Add(thing);

但此处的结果类型为:System.Web.Mvc.RedirectToRouteResultSystem.Web.Mvc.ActionResult)。它还没有执行Details方法。

我尝试在返回的对象上调用ExecuteResult传递一个模拟的ControllerContext,但框架对模拟对象中缺少细节感到不满。

我可以尝试填写详细信息等,但是我的测试代码比我正在测试的代码长,我觉得我需要单元测试进行单元测试!

我在测试哲学中遗漏了什么?当我无法进入返回状态时,如何测试此操作?

4 个答案:

答案 0 :(得分:31)

我现在正在使用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分钟。

答案 1 :(得分:12)

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的值进行比较,看看是否正常。

我不认为测试您的Details ActionResult以查看您的数据是否持久化是正确的方法。那不是单元测试,更像是功能测试。

但是你也应该对你的Details方法进行单元测试,以确保你的viewdata对象充满了来自你数据库的正确数据。

答案 2 :(得分:7)

你似乎为单元测试做了太多的事情。验证和数据访问通常由您从控制器操作调用的服务完成。你嘲笑那些服务,只测试它们被正确调用。

像这样(使用Rhino.Mocks&amp; 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"));
}

答案 3 :(得分:6)

我有一个测试重定向的静态帮助方法。

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