c# - Nunit 测试 MVC 站点

标签 c# asp.net testing nunit

我在尝试对我拥有的 MVC 站点进行单元测试时遇到了一些问题:我需要运行大量的 ASP.NET 环境(生成 httpcontexts、 session 、cookie、成员资格等)全面测试一切。

即使是测试一些不太前端的东西也需要成员(member)资格才能正常工作,而且手动欺骗这一切是很挑剔的。

有没有办法在 NUnit 测试中启动应用程序池?这似乎是最简单的方法。

最佳答案

如果编写得当,您不需要真实的上下文、真实的 session 、cookie 等。默认情况下,MVC 框架提供了一个可以模拟/ stub 的 HttpContext。我建议使用像 Moq 或 Rhino Mocks 这样的模拟框架,并创建一个 MockHttpContext 类,该类创建一个模拟上下文,其中包含您需要针对设置进行测试的所有属性。这是一个使用 Moq 的模拟 HttpContext

/// <summary>
/// Mocks an entire HttpContext for use in unit tests
/// </summary>
public class MockHttpContextBase
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
    /// </summary>
    public MockHttpContextBase() : this(new Mock<Controller>().Object, "~/")
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
    /// </summary>
    /// <param name="controller">The controller.</param>
    public MockHttpContextBase(Controller controller) : this(controller, "~/")
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
    /// </summary>
    /// <param name="url">The URL.</param>
    public MockHttpContextBase(string url) : this(new Mock<Controller>().Object, url)
    {              
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
    /// </summary>
    /// <param name="controller">The controller.</param>
    /// <param name="url">The URL.</param>
    public MockHttpContextBase(ControllerBase controller, string url)
    {
        HttpContext = new Mock<HttpContextBase>();
        Request = new Mock<HttpRequestBase>();
        Response = new Mock<HttpResponseBase>();
        Output = new StringBuilder();

        HttpContext.Setup(x => x.Request).Returns(Request.Object);
        HttpContext.Setup(x => x.Response).Returns(Response.Object);
        HttpContext.Setup(x => x.Session).Returns(new FakeSessionState());

        Request.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
        Request.Setup(x => x.QueryString).Returns(new NameValueCollection());
        Request.Setup(x => x.Form).Returns(new NameValueCollection());
        Request.Setup(x => x.ApplicationPath).Returns("~/");
        Request.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
        Request.Setup(x => x.PathInfo).Returns(string.Empty);

        Response.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
        Response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string path) => path);
        Response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(s => Output.Append(s));

        var requestContext = new RequestContext(HttpContext.Object, new RouteData());
        controller.ControllerContext = new ControllerContext(requestContext, controller);
    }

    /// <summary>
    /// Gets the HTTP context.
    /// </summary>
    /// <value>The HTTP context.</value>
    public Mock<HttpContextBase> HttpContext { get; private set; }

    /// <summary>
    /// Gets the request.
    /// </summary>
    /// <value>The request.</value>
    public Mock<HttpRequestBase> Request { get; private set; }

    /// <summary>
    /// Gets the response.
    /// </summary>
    /// <value>The response.</value>
    public Mock<HttpResponseBase> Response { get; private set; }

    /// <summary>
    /// Gets the output.
    /// </summary>
    /// <value>The output.</value>
    public StringBuilder Output { get; private set; }
}

/// <summary>
/// Provides Fake Session for use in unit tests
/// </summary>
public class FakeSessionState : HttpSessionStateBase
{
    /// <summary>
    /// backing field for the items in session
    /// </summary>
    private readonly Dictionary<string, object> _items = new Dictionary<string, object>();

    /// <summary>
    /// Gets or sets the <see cref="System.Object"/> with the specified name.
    /// </summary>
    /// <param name="name">the key</param>
    /// <returns>the value in session</returns>
    public override object this[string name]
    {
        get
        {
            return _items.ContainsKey(name) ? _items[name] : null;
        }
        set
        {
            _items[name] = value;
        }
    }
}

您可以进一步添加一些东西,例如 HTTP header 集合,但希望它能演示您可以做什么。

使用

var controllerToTest = new HomeController();
var context = new MockHttpContextBase(controllerToTest);

// do stuff that you want to test e.g. something goes into session

Assert.IsTrue(context.HttpContext.Session.Count > 0); 

关于成员(member)资格提供商或其他提供商,您遇到了一些难以测试的问题。我会抽象接口(interface)背后提供者的用法,这样您就可以在测试依赖它的组件时为接口(interface)提供伪造。但是,您仍然无法对使用提供程序的接口(interface)的具体实现进行单元测试,但是关于单元测试和代码覆盖率您想要/必须走多远,您的里程可能会有所不同。

关于c# - Nunit 测试 MVC 站点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7476041/

相关文章:

c# - 如何通过代码禁用 MVC Controller ?

c# - 使用 SQLite,如何搜索包含 '?

c# - 将日期字符串与当前日期进行比较?

c# - 如何使用 javascript 访问中继器内的 Div

asp.net - 使用服务器端分隔符时如何转义字符

R - Wald 检验和自相关检验

ruby-on-rails-3 - RSpec:最简单的测试失败并出现错误:预期 block 返回真值

c# - 如何修复树莓派上 GPIO 写入的内存映射失败异常

c# - 如何在代码隐藏文件中的 asp.net 表中插入 <a href> 标记?

java - Arquillian 测试未执行