c# - 如何对调用 Action 的 HTML Helper 进行单元测试?

标签 c# asp.net unit-testing mocking moq

我有一个用于基本 CMS 系统的 HTML Helper,它为特定内容对象调用 Controller 和操作。一个简化的例子如下:

public static MvcHtmlString RenderCMSObject(this HtmlHelper helper, CMSObject cmsObject)
{
    var actionName = string.IsNullOrEmpty(cmsObject.ActionName)
        ? "Index"
        : cmsObject.ActionName;

    var controllerName = string.IsNullOrEmpty(cmsObject.ControllerName)
        ? "Default"
        : cmsObject.ControllerName;

    return helper.Action(actionName, controllerName);
}

如何对它进行单元测试?

我可以创建一个 HtmlHelper,并模拟它的某些方面(例如 ViewBag)来测试返回字符串的 HTML 帮助程序(例如)。

对于上面的示例,它只需要一个可以模拟 Action 方法以返回特定内容的测试,这样就可以断言它是用正确的参数调用的。

深入研究 Action 的源代码,看起来测试必须是更复杂的集成测试和模拟路由、网络请求和 Controller 本身。

最佳答案

没有简单的方法来测试 helper.Action 是否调用了正确的参数。这是因为正如一些评论所指出的那样,HtmlHelper 也不是可模拟的,而 Action 也是一个静态扩展。但是您可以使用变通方法测试特定场景,例如根据您的问题。

it simply requires a test that could mock the Action method to return something in particular so it can be asserted it's being called with the right parameters.

虽然由于静态性质这不太可能,但我们可以使用不同的技术来验证是否使用正确的参数调用了 Action。

这只是指南,我还没有完全测试我的代码。我知道它会在测试上下文中工作,但我不完全确定这会在生产代码执行期间产生正确的结果。如果没有,我很乐意再看一遍..

首先,我将创建一个允许我覆盖 helper.Action 方法的类。

   public class HtmlHelperActionInvoker {
    public virtual MvcHtmlString InvokeAction(HtmlHelper helper, string action, string controller)      {
        return helper.Action(action, controller);
    }
}

然后在您的 Html 帮助程序类中,我将添加静态构造函数和一个委托(delegate)/函数,它允许我在测试执行期间设置我自己的 HtmlHelpActionInvoker 版本。

 public static class SomeHtmlHelperClass
 {
    static SomeHelperClass() {
        HtmlHelperActionFunc = () => new HtmlHelperActionInvoker();
    }

    public static Func<HtmlHelperActionInvoker> HtmlHelperActionFunc { get; set; }

    public static MvcHtmlString RenderCMSObject(this HtmlHelper helper, CMSObject cmsObject)
    {
        var actionName = string.IsNullOrEmpty(cmsObject.ActionName)
            ? "Index"
            : cmsObject.ActionName;

        var controllerName = string.IsNullOrEmpty(cmsObject.ControllerName)
            ? "Default"
            : cmsObject.ControllerName;

        var helperAction = HtmlHelperActionFunc();
        return helperAction.InvokeAction(helper, actionName, controllerName);
    }
  }

我相信静态 ctro 会执行一次,并在生产代码执行期间设置一次 HtmlHelperActionInvoker 的实例。后续请求将重用此实例。 (正如我之前提到的,您可能需要对此进行更多测试)

现在是单元测试

我们将使用一种称为 Extract & Override 的技术.

在您的测试区域中创建 HtmlHelperActionInvoker 的可测试版本。这不是假对象/ stub /模拟。这是 HtmlHelperActionInvoker 的可测试版本。并且仅用于使用预期参数调用的方法。

public class TesatableHtmlHelperAction : HtmlHelperActionInvoker
{
    public string Controller { get; set; }
    public string Action { get; set; }
    public override MvcHtmlString InvokeAction(HtmlHelper helper, string action, string controller) {
        Action = action;
        Controller = controller;

        return new MvcHtmlString("");
    }
}

在单元测试中,我们将设置可测试的 TesatableHtmlHelperAction,以便 SUT(被测系统)执行可测试版本。 (注意:它仍然被认为是真实的,因为我们覆盖了真实 HtmlHelperActionInvoker 的行为)

    [TestMethod]
    public void HtmlHelperActionRenderCMSObject_Execute_EnsureInvokeActionCalledWithExpectedControlerAndActionName()
    {            
        //Arrange
        var fakecmsObject = new CMSObject() { ActionName = "foo", ControllerName = "bar" };
        var testableHtmlHelperAction = new TesatableHtmlHelperAction();
        SomeHelperClass.HtmlHelperActionFunc = () => testableHtmlHelperAction;

        // Act
        SomeHelperClass.RenderCMSObject(null, fakecmsObject);

        // Verify
        Assert.AreEqual<string>(fakecmsObject.ActionName, testableHtmlHelperAction.Action);
        Assert.AreEqual<string>(fakecmsObject.ControllerName, testableHtmlHelperAction.Controller);
    }

关于c# - 如何对调用 Action 的 HTML Helper 进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20011394/

相关文章:

c# - 如何在此 C# 程序中获取要打印的列名?

c# - 如何证明某个版本的程序内存效率更高?

c# - ASP.NET 防止来自评论表单的机器人/垃圾邮件攻击

unit-testing - 有没有一个像 DB2 一样好的内存数据库

c# - 在 .NET Core 中使用 System.IO.Enumeration 自定义目录枚举

c# - 正则表达式删除字符串中的双/三逗号

asp.net - 通过 ExecuteResult 单元测试或运行时 ActionResult 字符串输出响应?

asp.net - 从代码隐藏中调用ASP.NET Web API

c# - 带有 Entity Framework 的 AutoFixture - 将 int 映射到枚举

unit-testing - 在 Laravel 5.1 中测试监听器