如何在UserManager类中模拟GetUserAsync方法?

时间:2018-02-19 18:23:19

标签: asp.net-mvc unit-testing asp.net-core asp.net-identity xunit.net

我正在尝试对在构造函数中接受UserManager对象的Reports控制器进行单元测试。

public class ReportController : BaseReportController {
    private readonly IUserService _userService;

    public ReportController ( IOptions<AppSettings> appSettings,
                              UserManager<ApplicationUser> userManager,
                              IUserService userService ) : base( appSettings, userManager ) {
        _userService = userService;
    }

    public async Task<ActionResult> Report ( string path ) {
        var currentUser = await GetCurrentUserAsync();
        var excludedItems = _userService.GetUserExcludedReportsById( currentUser.Id ).Select( er => er.Path );

        if ( string.IsNullOrEmpty( path ) || excludedItems.Any( path.Contains ) ) {
            return RedirectToAction( nameof(HomeController.Index), "Home" );
        }

        var customItems = _userService.GetUserCustomReportsById( currentUser.Id ).Select( er => er.Path );

        if ( path.Contains( AppSettings.CustomReportsFolderName ) && !customItems.Any( path.Contains ) ) {
            return RedirectToAction( nameof(HomeController.Index), "Home" );
        }


        var model = GetReportViewerModel( Request );
        model.Parameters.Clear();
        var dbname = _userService.GetDefaultDbName( (await GetCurrentUserAsync()).Id );
        model.Parameters.Add( "connectionStr", new[] {
            dbname
        } );
        //model.ReportPath = "/Portal Reports" + path;
        model.ReportPath = path;
        model.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Ntlm;

        return View( "Report", model );
    }
}

我想我需要以某种方式提供应用程序用户来调用它。所以GetCurrentUserAsync()将是第一个例子。这传递到基地:

public virtual Task<ApplicationUser> GetCurrentUserAsync() {
        return UserManager.GetUserAsync(HttpContext.User);
    }

我已经创建了一个FakeUserManager,但是在具有null异常的标识部分上仍然存在错误。这是我到目前为止的测试:

[Fact]
    public void ReportControllerReturnsToIndexIfPathIsNull()
    {
        //Arrange
        var context = new Mock<HttpContext>();
        context.Setup(x => x.User).Returns(user.Object);
        var mockUserService = new Mock<IUserService>();           
        AppSettings appSettings = new AppSettings() { };
        IOptions<AppSettings> options = Options.Create(appSettings);
        var mockUserStore = new Mock<IUserStore<ApplicationUser>>();          
        var sut = new ReportController(options, new FakeUserManager(mockUserStore.Object), mockUserService.Object);
        sut.HttpContext = context.Object;         
        //Act
        var result = sut.Report("");            
        //Assert           

    }
}

public class FakeUserManager : UserManager<ApplicationUser>
{
    public FakeUserManager(IUserStore<ApplicationUser> userStore)
        : base(userStore/*new Mock<IUserStore<ApplicationUser>>().Object*/,
              new Mock<IOptions<IdentityOptions>>().Object,
              new Mock<IPasswordHasher<ApplicationUser>>().Object,
              new IUserValidator<ApplicationUser>[0],
              new IPasswordValidator<ApplicationUser>[0],
              new Mock<ILookupNormalizer>().Object,
              new Mock<IdentityErrorDescriber>().Object,
              new Mock<IServiceProvider>().Object,
              new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
    {

    }

    public override Task<ApplicationUser> FindByIdAsync(string id)
    {
        return Task.FromResult(new ApplicationUser { Id = id });
    }
}

我在这里的右边线?我怎样才能传递这个httpcontext.user?非常感谢。

1 个答案:

答案 0 :(得分:2)

您在技术上不需要模拟GetUserAsync,如果,实际上更好。您只需要模拟HttpContext,以便返回正确的用户。

跳跃是一个非常容易的障碍,而实际上提供了更好的测试。如果您模拟GetUserAsync,则表示您正在测试某个操作的特定实现,即使用GetUserAsync获取用户的操作。如果您以后决定让用户通过某个服务类,直接从您的上下文等,那么您的测试将会中断。

同时,只要您根据HttpContext查询当前用户,任何方法显然都会使用,因为那是“当前”用户的概念所在,那么您就不会依赖于任何一个实现。只要动作以某种方式获得当前用户,它就可以工作。

<强>更新

var controller = new ReportController();
controller.ControllerContext = new ControllerContext
{
    HttpContext = new DefaultHttpContext
    {
        User = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
        {
            new Claim(ClaimTypes.Name, "foo")
        }));
    }
};

您可能需要根据用户设置的声明进行一些操作,具体取决于您的代码正在执行的操作。以上用于设置User.Identity.Name的值,并且应该足以满足大多数目的。标准声明的完整列表为here

相关问题