我正在尝试为使用用户管理器和角色管理器的控制器编写单元测试。
我有一些操作,涉及创建用户,创建角色,在角色之间添加和删除用户。
我正在通过控制器构造函数将用户管理器signInManager作为依赖项传递。
private readonly IHostingEnvironment hostingEnvironment;
public IHostingEnvironment HostingEnvironment => hostingEnvironment;
public AdminController(IHostingEnvironment environment, SignInManager<IdentityUser> signInManager, ILogger<LoginModel> logger, RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager)
{
_signInManager = signInManager;
_logger = logger;
_roleManager = roleManager;
_userManager = userManager;
hostingEnvironment = environment;
}
我需要测试用户是否已成功添加到角色,是否已从角色中删除等。
我不想测试服务本身,而只是测试操作。
我已按如下所示分离操作以捕获任何异常。
public async Task<bool> AddUserToRole(IdentityUser user, string role)
{
try
{
var addUserToRole = await _userManager.AddToRoleAsync(user, role);
if (addUserToRole.Succeeded)
{
return true;
}
else
{
return false;
}
}
catch(Exception e)
{
throw new Exception("OperationFailed");
}
return false;
}
类似地,我已经取消了涉及userManager的所有操作,并在控制器动作中调用了上述函数。
对于测试,我已经执行了以下操作:
[TestFixture]
public class AdminControllerTest
{
protected TestContext db;
protected Context _db;
protected SignInManager<IdentityUser> signInManager;
protected ILogger<LoginModel> logger;
protected RoleManager<IdentityRole> roleManager;
protected UserManager<IdentityUser> userManager;
protected IHostingEnvironment hostingEnvironment;
[TestCase]
public void Verify_AdminController_Is_Decorated_With_Authorize_Attribute()
{
var userEmail = _db.AspNetUsers.Select(x => x.Email).FirstOrDefault();
var user =await userManager.FindByEmailAsync(userEmail);
var userRole = "Supervisor";
AdminController adminController = new AdminController(hostingEnvironment, signInManager, logger, roleManager, userManager);
var actionResult = adminController.AddUserToRole(user, userRole).Result;
Assert.IsTrue(actionResult);
}
以上操作无效。
我尝试使用Moq来模拟服务。但是,如果我要订购这些服务,就无法将它们传递给控制器实例。
如果我嘲笑控制器本身;我无法使用它的动作;返回Null;
我不完全理解模拟的概念。
解决上述问题的最佳方法是什么。 我正在使用Asp.Net Core 2.1和Nunit 3.0
谢谢:)
答案 0 :(得分:1)
Its looks like you have not yet decoupled the dependencies out from the AdminController
constructor.
Instead of passing the implementation you will need to pass the abstraction/interfaces:
public AdminController(IHostingEnvironment environment,
ISignInManager<IdentityUser> signInManager,
ILogger<LoginModel> logger,
IRoleManager<IdentityRole> roleManager,
IUserManager<IdentityUser> userManager)
Note that I've only added the I
prefix, but it mean will need to refactor your code to pass an interfaces as I mentioned.
Now, we can mock the AdminController
easily with:
[TestFixure]
public class AdminControllerTests
{
private AdminController _adminController;
private IHostingEnvironment _hostingEnvironment = new Mock<IHostingEnvironment>();
private ISignInManager<IdentityUser> _signInManager = new Mock<ISignInManager<IdentityUser>>();
The _signInManager
will need a setup that uses the method that returns ISignInManager within your ISignInManager
, lets assume its name its Builder()
.
*Note there is other ways of mocking, here are two ways, or consider using autofac
[SetUp]
public void SetUp()
{
_signInManager.Setup(a => a.Builder()).Returns(new[] { new IdentityUser() });
//Do the same for the rest of the dependencies.
//...
_adminController = new AdminController(_hostingEnvironment, _signInManager.Object, ...);
}
Now you can make use of the _adminController
instance (See proper naming convention for test methods):
[Test]
public void Verify_AdminController_Is_Decorated_With_Authorize_Attribute()
{
//...
var actionResult = _adminController.AddUserToRole(user, userRole).Result;
//...
}
}