我正在尝试对一些依赖于 UserManager
和 RoleManager
的方法进行单元测试,但遇到了一些困难。
我应该模拟 UserManager
和 RoleManager
然后将其传递给 AdminController
吗?或者我应该首先访问 AccountController
的默认 SignIn
操作并进行身份验证。我不确定如何执行这两个选项或解决此问题的最佳方法是什么。
当不验证/实例化管理器时,我在 UserManager
我的测试
[Test]
public void MaxRole_SuperAdmin()
{
var adminController = new AdminController();
var maxRole = adminController.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
Controller 和方法
[Authorize(Roles = "APGame Admin, APGame Investigator")]
[RequireHttps]
public class AdminController : Controller
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
private set { _userManager = value; }
}
private ApplicationRoleManager roleManager;
public ApplicationRoleManager RoleManager
{
get
{
return roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set { roleManager = value; }
}
public string GetMaxRole(string userId)
{
IEnumerable<string> userRoles = UserManager.GetRoles(userId);
string role = null;
if (userRoles.Contains("APGame Admin"))
{
if (userRoles.Contains("TeVelde Group") && userRoles.Contains("Genomics Group"))
role = "Super Admin";
else role = "Admin";
}
else if (userRoles.Contains("APGame Investigator"))
{
role = "Investigator";
}
else if (userRoles.Contains("APGame User"))
{
role = "User";
}
else
{
//TODO: Log no role, HIGH
}
return role;
}
}
最佳答案
如果你关注了我的博文,你应该知道 ApplicationUserManager
的构造函数是这样的:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store)
{
// configuration-blah-blah
}
}
并且您的 Controller 应该将用户管理器对象注入(inject)到构造函数中:
public class AdminController : Controller
{
private readonly ApplicationUserManager userManager;
public AdminController(ApplicationUserManager userManager)
{
this.userManager = userManager;
}
}
现在进行测试 - 您需要一个模拟框架。几年前,我曾经在每个测试中都放入 MOQ,现在首选的模拟框架是 NSubstitue,因为它的语法更具可读性。目前我很少使用模拟替代品并且更喜欢集成测试,甚至是深入研究数据库,但这不是这个问题/讨论的目标。
因此,为了进行测试,您需要创建被测系统 (SUT) - AdminController
:
[Test]
public void MaxRole_SuperAdmin()
{
var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>();
var userManager = new ApplicationUserManager(userStoreStub);
var sut = new AdminController(userManager);
//TODO set up method substitutions on userStoreStub - see NSubstitute documentation
var maxRole = sut.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
但这是非常笨拙的测试。您正在尝试测试太深的东西。我建议您将 GetMaxRole
记录从 Controller 移到一个单独的类中 - 一个服务类,或者这可以是 ApplicationUserManager
的一部分,也可以是 QueryHandler
如果你愿意的话。无论你怎么调用它,它都不应该真正成为 Controller 的一部分。我认为这个方法实际上属于ApplicationUserManager
。这样你的测试层就减少了一个,你只需要创建用户管理器类,而不是 Controller 。
鉴于 GetMaxRole()
方法是 ApplicationUserManager
的一部分,您的测试将变得更小:
[Test]
public void MaxRole_SuperAdmin()
{
var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>();
var sut = new ApplicationUserManager(userStoreStub);
//TODO set up method substitutions on userStoreStub - see NSubstitute documentation
var maxRole = sut.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
将测试方法从 Controller 中移出的原因如下:
- 随着时间的推移,我发现 Controller 很容易随着新的依赖项的添加/删除/替换而经常更改。而且,如果您围绕 Controller 进行了一些测试,则必须更改每个测试更改的依赖项。
- 如果您要在
AdminController
之外的其他地方使用GetMaxRole
方法,您就有麻烦了。除了提供 HTTP(S) 端点之外, Controller 并不意味着共享方法。 - 您方法的逻辑看起来像域逻辑或业务逻辑。这应该彻底测试。 Controller 不容易测试,因为它们处理 HTTP 请求和
HttpContext
,这不容易测试(尽管可能)。此外,最好避免将 Controller 的东西与业务逻辑混合 - 帮助您避免意大利面条代码综合症。
我可以一直讨论这个话题,但最好阅读 DI book by Mark Seeman和 Unit Testing book by Roy Osherove .
关于c# - 单元测试依赖于 UserManager 和 RoleManager,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35733185/