如何模拟HttpContext访问IIdentity的扩展方法?

时间:2019-02-25 16:12:23

标签: c# asp.net-mvc unit-testing asp.net-mvc-5

在我的MVC项目中,我正在为控制器创建单元测试以返回视图。

此控制器的操作结果将构建一个视图模型,并且该视图模型的构造函数将调用

HttpContext.Current.User.Identity.NameWithoutDomain().ToUpper();

NameWithoutDomainIIdentity的扩展方法。

每当我运行它时,都会不断显示“找不到对象”,我很好奇如何为单元测试适当地模拟它?

扩展方法:

public static class SystemWebExtension
{
    public static string NameWithoutDomain(this IIdentity identity)
    {
        return identity.Name.Split('\\').Last();
    }
}

模型构造函数:

public Model()
{
    this.PreparedByUser = HttpContext.Current.User.Identity.NameWithoutDomain().ToUpper();
    this.AuthorizedBy = true;
    this.AuthorizedByUser = HttpContext.Current.User.Identity.NameWithoutDomain().ToUpper();
    this.IssueCredit = true;
    this.CreateUser = string.IsNullOrEmpty(this.CreateUser) ? Environment.UserName.ToLower() : this.CreateUser;
    this.CreateDate = this.CreateDate.HasValue ? this.CreateDate : DateTime.Now;
    this.UpdateUser = string.IsNullOrEmpty(this.CreateUser) ? null : Environment.UserName.ToLower();
    this.UpdateDate = this.CreateDate.HasValue ? DateTime.Now : (DateTime?) null;
}

2 个答案:

答案 0 :(得分:0)

避免耦合到HttpContext,因为在单元测试时不可用。

您应该将必要的值明确注入模型中

public Model(IIdentity identity) {
    this.PreparedByUser = identity.NameWithoutDomain().ToUpper();
    this.AuthorizedBy = true;
    this.AuthorizedByUser = identity.NameWithoutDomain().ToUpper();
    this.IssueCredit = true;
    this.CreateUser = string.IsNullOrEmpty(this.CreateUser) ? Environment.UserName.ToLower() : this.CreateUser;
    this.CreateDate = this.CreateDate.HasValue ? this.CreateDate : DateTime.Now;
    this.UpdateUser = string.IsNullOrEmpty(this.CreateUser) ? null : Environment.UserName.ToLower();
    this.UpdateDate = this.CreateDate.HasValue ? DateTime.Now : (DateTime?) null;
}

使用控制器后,您将可以在其属性中访问所需的信息

//...

var model = new Model(this.User.Identity);

return View(model);

在测试时,您可以通过其控制器上下文配置控制器

//...
var username = "some username here"
var identity = new GenericIdentity(username, "");
identity.AddClaim(new Claim(ClaimTypes.Name, username));
var principal = new GenericPrincipal(identity, roles: new string[] { });
var user = new ClaimsPrincipal(principal);

var context = new Mock<HttpContextBase>();
context.Setup(_ => _.User).Returns(user);

//...

controller.ControllerContext = new ControllerContext(context.Object, new RouteData(),  controller );

答案 1 :(得分:0)

您不能模拟扩展方法,您必须模拟扩展方法内部发生的事情,以便它返回您想要的值。

要确保NameWithoutDomain返回您想要的内容,可以模拟IIdentity并使其返回所需的用户名,如下所示:

var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("UsernameWithoutDomain");

如果您要测试NameWithoutDomain是否完成了预期的工作(即拆分字符串),则可以编写一个单独的测试来调用此扩展方法。

对于HttpContext,您可以在创建控制器之前实例化它,然后将经过模拟的IIdentity传递给它,例如(通过WebAPI2测试的 ):< / p>

var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("UsernameWithoutDomain");

HttpContext.Current =
    new HttpContext(new HttpRequest(null, "http://test.com", null), new HttpResponse(null))
    {
        User = new GenericPrincipal(identityMock.Object, new[] {"Role1"})
    };

var myController = new AccountController();

然后,当您创建控制器实例进行测试时,HttpContext.Current.User.Identity.Name将返回UsernameWithoutDomain。测试愉快! :-)