我有一个单元测试夹具,我正在尝试在ASP.NET MVC控制器上测试一个ControllerAction,该控制器用于Web应用程序的成员函数。我正在尝试模拟测试的HttpContext。正在测试的ControllerAction实际上设置了HttpContext的属性,例如Session值,Response.Cookies值等。这不是所有代码,但这里是我试图运行的测试的粗略示例:
[Test]
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser()
{
var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock};
context.SetupAllProperties();
var provider = new Mock<MembershipProvider>(new object[] {context.Object});
var controller = new AccountController(context.Object, provider.Object);
// This just sets up a local FormCollection object with valid user data
// in it to use to attempt the registration
InitializeValidFormData();
ActionResult result = controller.Register(_registrationData);
Assert.IsInstanceOfType(typeof(ViewResult), result);
// Here is where I'd like to attempt to do Assertions against properties
// of the HttpContext, like ensuring that a Session object called "User"
// exists, and new auth cookie exists on the Response.Cookies collection.
// So far I've been unable to successfully check the values of those properties.
// I've been unsuccessful in getting those properties setup correctly on my
// mock object so that my ControllerAction can actually *set* their values,
// and that I can make assertions on them afterwards. The above code actually
// generates a StackOverflowException (which I've reported) on the
// context.SetupAllProperties() call. What am I doing wrong, or what do I need
// to do to be able to set and assert on those context properties?
}
不确定我做错了什么,但如果有人能指出我正确的方向并告诉我如何设置这个模拟HttpContextBase对象,以便我的控制器可以实际设置其属性的值,我会喜欢它,并且我可以对这些属性进行断言,以确保我的ControllerAction正在执行我需要的操作。
我接近这个错误的方式吗?我知道MVC控制器有一个ControllerContext,我可以使用它来设置Session等的值,但我无法弄清楚如何在不注入的情况下模拟这样的东西。有没有办法做到这一点? (我还需要能够将上下文传递给我的MembershipProvider)这会是一个更好的方法吗?
感谢。
答案 0 :(得分:33)
我正在使用他的Pro Asp.NET MVC book中包含的一些代码史蒂夫·桑德森的版本......我目前正处于道德困境,是否可以在此处发布代码。我如何与高度剥离的版本妥协? ;)
所以这可以很容易地重复使用,创建一个类似下面的类,你将通过你的控制器。这将设置你的模拟并将它们设置为控制器的ControllerContext
public class ContextMocks
{
public Moq.Mock<HttpContextBase> HttpContext { get; set; }
public Moq.Mock<HttpRequestBase> Request { get; set; }
public RouteData RouteData { get; set; }
public ContextMocks(Controller controller)
{
//define context objects
HttpContext = new Moq.Mock<HttpContextBase>();
HttpContext.Setup(x => x.Request).Returns(Request.Object);
//you would setup Response, Session, etc similarly with either mocks or fakes
//apply context to controller
RequestContext rc = new RequestContext(HttpContext.Object, new RouteData());
controller.ControllerContext = new ControllerContext(rc, controller);
}
}
然后在您的测试方法中,您只需创建一个ContextMocks实例并传入您正在测试的控制器对象:
[Test]
Public void test()
{
var mocks = new ContextMocks(controller);
var req = controller.Request;
//do some asserts on Request object
}
看起来与Craig的例子非常相似,但这与Moq v3有关。我必须为史蒂夫桑德森提供道具 - 我使用它作为测试各种传统上难以测试的东西的基础:饼干,会话,请求方法,查询字符串等等!
答案 1 :(得分:23)
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
request.Expect(req => req.ApplicationPath).Returns("~/");
request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/");
request.Expect(req => req.PathInfo).Returns(string.Empty);
response.Expect(res => res.ApplyAppPathModifier(It.IsAny<string>()))
.Returns((string virtualPath) => virtualPath);
user.Expect(usr => usr.Identity).Returns(identity.Object);
identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true);
context.Expect(ctx => ctx.Request).Returns(request.Object);
context.Expect(ctx => ctx.Response).Returns(response.Object);
context.Expect(ctx => ctx.Session).Returns(session.Object);
context.Expect(ctx => ctx.Server).Returns(server.Object);
context.Expect(ctx => ctx.User).Returns(user.Object);
return context.Object;
}
这是the MvcMockHelpers library released by Scott Hanselman的增强版。这是Moq 2.0代码;语法在3中略有不同。