单元测试依赖于用户管理器和角色管理器
本文关键字:管理器 角色 用户 依赖于 单元测试 | 更新日期: 2023-09-27 18:36:54
我正在尝试对一些依赖于UserManager
和RoleManager
的方法进行单元测试,并且遇到了一些困难。
我应该模拟UserManager
和RoleManager
,然后将其传递给AdminController
?还是应该首先访问AccountController
的默认SignIn
操作并进行身份验证。我不确定如何同时执行这两种选择,或者解决此问题的最佳方法是什么。
当不对管理器进行身份验证/实例化时,我在UserManager
上得到 NullReferenceExceptions
我的测试
[Test]
public void MaxRole_SuperAdmin()
{
var adminController = new AdminController();
var maxRole = adminController.GetMaxRole(SuperAdminUserId);
Assert.AreEqual(maxRole, "Super Admin");
}
控制器和方法
[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
}
}
并且您的控制器应该将用户管理器对象注入到构造函数中:
public class AdminController : Controller
{
private readonly ApplicationUserManager userManager;
public AdminController(ApplicationUserManager userManager)
{
this.userManager = userManager;
}
}
现在开始你的测试 - 你需要一个模拟框架。几年前,我曾经在每次测试中都放置最小起订量,现在模拟偏好框架是 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
记录从控制器移到一个单独的类中 - 服务类,或者这可以是ApplicationUserManager
的一部分,或者如果您愿意,也可以是QueryHandler
。不管你怎么称呼它,它都不应该真正成为控制器的一部分。我认为这种方法实际上属于ApplicationUserManager
.这样,您的测试层减少了一个,您只需要创建用户管理器类,而不是控制器。
鉴于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");
}
将测试方法移出控制器的原因如下:
- 随着时间的推移,我发现控制器很容易经常被更改,添加新的依赖项/删除/替换。如果你对控制器有一些测试,你将不得不改变每个测试,改变依赖关系的使用。
- 如果你要在
AdminController
以外的其他地方使用GetMaxRole
方法,你就有麻烦了。控制器不用于共享提供 HTTP(S) 终结点以外的方法。 - 方法的逻辑类似于域逻辑或业务逻辑。这应该彻底测试。控制器不容易测试,因为它们处理HTTP请求和
HttpContext
测试起来并不简单(尽管可能)。此外,最好避免控制器与业务逻辑混合 - 帮助您避免意大利面条代码综合症。
你应该模拟用户管理器和角色管理器,并将它们传递给管理员控制器