C# Nunit测试MVC站点

C# Nunit测试MVC站点,c#,asp.net,testing,nunit,C#,Asp.net,Testing,Nunit,我在尝试对我拥有的MVC站点进行单元测试时遇到了一点问题:我需要运行大量的ASP.NET环境(生成HttpContext、会话、cookie、成员身份等),以全面测试所有内容 即使是测试一些不太前端的东西,也需要会员资格才能正常工作,而且手工完成这些东西也很难 有没有办法在NUnit测试中启动应用程序池?这似乎是最简单的方法。我不知道有什么方法可以做到这一点,因为您的代码不在该过程中,并且需要一个不在aspnet中的主机。(虽然我以前是错的哈哈) Phil Haack有一个更老的HttpSimu

我在尝试对我拥有的MVC站点进行单元测试时遇到了一点问题:我需要运行大量的ASP.NET环境(生成HttpContext、会话、cookie、成员身份等),以全面测试所有内容

即使是测试一些不太前端的东西,也需要会员资格才能正常工作,而且手工完成这些东西也很难


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

我不知道有什么方法可以做到这一点,因为您的代码不在该过程中,并且需要一个不在aspnet中的主机。(虽然我以前是错的哈哈)

Phil Haack有一个更老的HttpSimulator,你试过了吗


如果编写正确,您不需要真正的上下文、真正的会话、cookie等。默认情况下,MVC框架提供了可以模拟/存根的HttpContext。我建议使用Moq或rhinomocks之类的模拟框架,并创建一个MockHttpContext类,该类创建一个模拟上下文,其中包含您需要测试的所有属性。下面是一个使用


关于会员资格提供商或其他提供商,您遇到了一些难以测试的问题。我将抽象接口后面的提供者的用法,以便在测试依赖它的组件时,可以为接口提供一个假接口。但是,对使用提供程序的接口的具体实现进行单元测试仍然会有困难,但是在单元测试和代码覆盖率方面,您的里程可能会有所不同

您需要为这些服务构建包装器接口。最初的MVC2和MV3初学者项目模板默认情况下做到了这一点,但由于某些原因,它们在最新版本中放弃了这一点

您可以尝试查找原始AccountController代码的示例,为您提供一个起点。他们使用IMembershipService和iFormasAuthenticationService


模拟会话、上下文等相对简单

看看MVCContrib项目(http://mvccontrib.codeplex.com/)因为他们有一个助手来创建填充了所有上下文对象(如HttpContext)的控制器。

是的,我有,它有助于解决一些问题,但成员身份完全不可用,web.config没有加载,而是手动强制进入(这可能是我许多问题的根源),它要求我调整我的HttpApplication类,以便我可以将上下文/会话强制添加到MVC应用程序类中。这里有很多强制操作,可能会中断,我想知道是否有更好的方法。更好的方法是以一种可以模拟包含所需信息的设置类的方式来构建应用程序需要。会员资格提供商也是如此,你应该围绕其功能包装一个界面,然后伪造。如果你想查看,MVC3默认互联网应用程序模板会这样做。哈..每个人基本上都说了和上次评论一样的话。我应该说“参考上面的内容”:
System.Web
引入了专门创建的etc类,允许您在单元测试期间进行模拟。您的代码应该处理基类的实例,然后模拟这些实例。>“MVC框架默认提供可以模拟/存根的HttpContext。”,当我模拟时,它会自动替换HttpContext吗?还是必须使用抽象代码?主要的问题是一年多的代码,如果需要的话,可以全部修改。@StangeWill=casperOne对您的问题的评论就是我所指的。如上所示,HttpContextBase可以很容易地模拟。
/// <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;
        }
    }
}
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);