我正在尝试对在构造函数中接受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?非常感谢。
答案 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。