使用 Windows 身份验证在 WebAPI 控制器上进行单元测试

UnitTest on WebAPI controller with Windows Authentication

我有一个 api,里面的方法很少。在访问任何控制器方法之前,我需要使用 Windows Authentication 对用户进行身份验证。为此,我编写了一个私有方法来获取 UserIdentity 并将其分配给私有 bool 变量。

每个控制器方法都会首先检查bool 来验证用户,如果失败,将401 抛回给前端。

在将单元测试用例写入控制器方法时,我不确定如何模拟 UserIdentity 或者我是否应该更新控制器以允许测试 UserIdentity

控制器:

public class DefaultController : ApiController
{
  private bool isUsrAuthenticated;

  public DefaultController()
  {
    AuthenticateUser();
  }

  private void AuthenticateUser()
  {
    isUsrAuthenticated = User.Identity.IsAuthenticated;
  }

  [HttpGet]
  public IHttpActionResult GetProducts()
  {
    if (isUsrAuthenticated) { // do something }
    else { // throw 401 }
  }

  [HttpPost]
  public IHttpActionResult Update()
  {
    if (isUsrAuthenticated) { // do something }
    else { // throw 401 }
  }
}

单元测试结果总是return 401.

在 run-time ApiController.User 属性 是在调用控制器的构造函数之后设置的。这意味着您当前在构造函数中调用它的流程如

public DefaultController() {
    AuthenticateUser();
}

有缺陷,不会提供预期的行为。

先修改代码,再看单元测试。

使用 属性 代替

private bool IsUserAuthenticated {
    get {
        return User.Identity.IsAuthenticated;
    }
}

并相应地重构代码

public class DefaultController : ApiController {

    private bool IsUserAuthenticated {
        get {
            return User.Identity.IsAuthenticated;
        }
    }

    [HttpGet]
    public IHttpActionResult GetProducts() {
        if (IsUserAuthenticated) { // do something }
        else { // throw 401 }
    }

    [HttpPost]
    public IHttpActionResult Update() {
        if (IsUserAuthenticated) { // do something }
        else { // throw 401 }
    }

    //...

}

现在,为了测试ApiController,可以在安排单元测试时设置User 属性,以便测试按预期执行

例如

[TestMethod()]
public void DefaultController_Should_GetProducts() {
    //Arrange
    var username = "name_here";
    var userId = 2;

    var identity = new GenericIdentity(username, "");
    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
    identity.AddClaim(new Claim(ClaimTypes.Name, username));

    var principal = new GenericPrincipal(identity, roles: new string[] { });
    var user = new ClaimsPrincipal(principal);

    var controller = new DefaultController() {
        User = user //<-- Set the User on the controller directly
    };

    //Act
    var actionResult = controller.GetProducts();

    //Assert
    //...
}