我正在尝试对依赖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;
}
}
答案 0 :(得分:2)
如果您关注我的博文,您应该为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;
}
}
现在进行测试 - 你需要一个模拟框架。几年前,我曾经在每一次测试中都使用 MOQ ,现在,由于语法更具可读性,现在模拟优先选择框架是 NSubstitue 。当前时间我很少使用模拟替代品并且更喜欢集成测试,甚至更喜欢使用DB,但这不是这个问题/讨论的目标。
因此,对于您的测试,您需要创建被测系统(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");
}
将测试方法从控制器中移出的原因如下:
GetMaxRole
之外的其他地方使用AdminController
方法,则您遇到了麻烦。控制器并不意味着共享除提供HTTP(S)端点之外的方法。HttpContext
,这不容易测试(尽管可能)。此外,最好避免将控制器内容与业务逻辑混合在一起 - 帮助您避免使用意大利面条代码综合症。 我可以继续关注这个话题,但最好阅读DI book by Mark Seeman和Unit Testing book by Roy Osherove。
答案 1 :(得分:1)
您应该模拟UserManager和RoleManager并将它们传递给AdminController