单元测试FormsAuthentication.SignOut()

时间:2017-01-17 07:30:02

标签: c# unit-testing mocking httprequest forms-authentication

在运行我的单元测试方法时,我收到错误FormsAuthentication.SignOut()。我像这样嘲笑httpcontext

var httpRequest = new HttpRequest("", "http://localhost/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer(
    "id",
    new SessionStateItemCollection(),
    new HttpStaticObjectsCollection(),
    10,
    true,
    HttpCookieMode.AutoDetect,
    SessionStateMode.InProc,
    false);
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
var controller = new AccountController();
var requestContext = new RequestContext(new HttpContextWrapper(httpContext), new RouteData());
controller.ControllerContext = new ControllerContext(requestContext, controller);
var actual = controller.Login(new CutomerModel() { Login = "admin", Password = "Password1" });
return httpContext;

在登录方法

public ActionResult Login(CutomerModel obj)
{
    FormsAuthentication.SignOut();
}

FormsAuthentication.SignOut();抛出

  

'对象引用未设置为对象的实例。 “

1 个答案:

答案 0 :(得分:3)

静态方法FormsAuthentication.SignOut依赖于另一个静态成员HttpContext.Current,它在单元测试期间不可用。将控制器紧密耦合到静态的HttpContext.Current会导致代码很难测试。尽量避免耦合到静态调用。

附注:难以为代码设置单元测试,这是一个明确的迹象,表明需要对其进行审核并且很可能会重构。

Abstract FormsAuthentication调用他们自己的关注/接口,以便他们可以被模拟。

public interface IFormsAuthenticationService {

    void SignOut();

    //...other code removed for brevity
}

生产代码可以包装实际调用,该调用应该可用HttpContext.Current。确保DI容器知道如何解决依赖关系。

public class FormsAuthenticationService : IFormsAuthenticationService {

    public void SignOut() {
        FormsAuthentication.SignOut();
    }

    //...other code removed for brevity

}

重构控制器以依赖抽象而不是实现问题。

public class AccountController : Controller {

    //...other code removed for brevity.

    private readonly IFormsAuthenticationService formsAuthentication;

    public AccountController(IFormsAuthenticationService formsAuthentication) {
        //...other arguments removed for brevity
        this.formsAuthentication = formsAuthentication;
    }

    public ActionResult Login(CutomerModel obj) {
        formsAuthentication.SignOut();
        //...
        return View();
    }

    //...other code removed for brevity.
}

和示例测试

注意:我使用Moq来模拟依赖项,使用FluentAssertions来断言结果。

[TestMethod]
public void LoginTest() {
    //Arrange
    var model = new CutomerModel() { Login = "admin", Password = "Password1" };        
    var mockFormsAuthentication = new Mock<IFormsAuthenticationService>();

    var controller = new AccountController(mockFormsAuthentication.Object);

    //Act
    var actual = controller.Login(model) as ViewResult;

    //Assert (using FluentAssertions)
    actual.Should().NotBeNull(because: "the actual result should have the returned view");
    mockFormsAuthentication.Verify(m => m.SignOut(), Times.Once);
}