C# 对依赖于UserManager的控制器进行单元测试的最佳实践<;TUser>;?
我有一个具有以下签名的控制器:C# 对依赖于UserManager的控制器进行单元测试的最佳实践<;TUser>;?,c#,unit-testing,moq,xunit,asp.net-core-2.1,C#,Unit Testing,Moq,Xunit,Asp.net Core 2.1,我有一个具有以下签名的控制器: [Route("api/[controller]")] [ApiController] public class UsersController : ControllerBase { private ILogger<UsersController> _logger; private readonly UserManager<IdentityUser> _usermanager; public UsersContr
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private ILogger<UsersController> _logger;
private readonly UserManager<IdentityUser> _usermanager;
public UsersController(ILogger<UsersController> logger, UserManager<IdentityUser> usermanager)
{
_usermanager = usermanager;
_logger = logger;
}
[HttpGet("{_uniqueid}")]
public async Task<ObjectResult> GetUser(string _uniqueid)
{
//Retrieve the object
try
{
var user = await _usermanager.FindByIdAsync(uniqueid);
var model = JsonConvert.DeserializeObject<GetUserModel>(user.ToString());
return new ObjectResult(JsonConvert.SerializeObject(model));
}
catch(CustomIdentityNotFoundException e)
{
return new BadRequestObjectResult(("User not found: {0}", e.Message));
}
}
}
[路由(“api/[控制器]”)]
[ApiController]
公共类UsersController:ControllerBase
{
私人ILogger_记录器;
私有只读用户管理器_UserManager;
公共用户控制器(ILogger记录器、UserManager UserManager)
{
_usermanager=usermanager;
_记录器=记录器;
}
[HttpGet(“{u uniqueid}”)]
公共异步任务GetUser(字符串\u uniqueid)
{
//检索对象
尝试
{
var user=await\u usermanager.FindByIdAsync(uniqueid);
var model=JsonConvert.DeserializeObject(user.ToString());
返回新的ObjectResult(JsonConvert.SerializeObject(model));
}
捕获(CustomIdentityNotFounde异常)
{
返回新的BadRequestObjectResult((“未找到用户:{0}”,e.Message));
}
}
}
现在,我的单元测试如下所示:
public class UsersUnitTests
{
public UsersController _usersController;
private UserManager<IdentityUser> _userManager;
public UsersUnitTests()
{
_userManager = new MoqUserManager<IdentityUser>();
_usersController = new UsersController((new Mock<ILogger<UsersController>>()).Object, _userManager);
}
[Fact]
public async Task GetUser_ReturnsOkObjectResult_WhenModelStateIsValid()
{
//Setup
//Test
ObjectResult response = await _usersController.GetUser("realuser");
//Assert
//Should receive 200 and user data content body
response.StatusCode.Should().Be((int)System.Net.HttpStatusCode.OK);
response.Value.Should().NotBeNull();
}
}
公共类usersunitests
{
公共用户控制器\u用户控制器;
私人用户管理器(UserManager);;
公共用户sunitests()
{
_userManager=新的MoqUserManager();
_usersController=newusersController((new Mock()).Object,\u userManager);
}
[事实]
公共异步任务GetUser\u ReturnsOkObjectResult\u WhenModelStateIsValid()
{
//设置
//试验
ObjectResult response=wait_usersController.GetUser(“realuser”);
//断言
//应接收200和用户数据内容体
response.StatusCode.Should().Be((int)System.Net.HttpStatusCode.OK);
response.Value.Should().NotBeNull();
}
}
以及最低起订量课程:
public class MoqUserManager<T> : UserManager<IdentityUser>
{
public MoqUserManager(IUserStore<IdentityUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<IdentityUser> passwordHasher, IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<IdentityUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public MoqUserManager()
: base((new MoqUserStore().Store), new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<IdentityUser>>().Object, new Mock<IEnumerable<IUserValidator<IdentityUser>>>().Object,
new Mock<IEnumerable<IPasswordValidator<IdentityUser>>>().Object, new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object, new Mock<IServiceProvider>().Object, new Mock<ILogger<UserManager<IdentityUser>>>().Object)
{
}
}
public class MoqUserStore : IdentityUserStore
{
private Mock<IdentityUserStore> _store;
public MoqUserStore()
:base(new Mock<IdentityDbContext>().Object, new Mock<ILogger<IdentityUserStore>>().Object, null)
{
_store = new Mock<IdentityUserStore>(new Mock<IdentityDbContext>().Object, new Mock<ILogger<IdentityUserStore>>().Object, null);
_store.Setup(x => x.FindByIdAsync("realuser", default(CancellationToken))).Returns(Task.Run(() => new IdentityUser("realuser")));
_store.Setup(x => x.FindByIdAsync("notrealuser", default(CancellationToken))).Throws(new CustomIdentityNotFoundException());
_store.Setup(x => x.CreateAsync(new IdentityUser("realuser"), default(CancellationToken))).Returns(Task.Run(() => IdentityResult.Success));
}
public IdentityUserStore Store { get => _store.Object; }
}
公共类MoqUserManager:UserManager
{
公共MoqUserManager(IUserStore商店、IOptions选项访问器、,
IPasswordHasher密码hasher,IEnumerable用户验证程序,
IEnumerable PasswordValidator、ILookupNormalizer、keyNormalizer、,
IdentityErrorDescriptber错误,iSeries虚拟Provider服务,ILogger记录器)
:base(存储、选项访问器、密码哈希器、用户验证器、密码验证器、密钥规范化器、错误、服务、记录器)
{
}
公共MoqUserManager()
:base((新MoqUserStore().Store),新Mock().Object,
新建Mock().Object,新建Mock().Object,
新建Mock().Object,新建Mock().Object,
新建Mock().Object,新建Mock().Object,新建Mock().Object)
{
}
}
公共类MoqUserStore:IdentityUserStore
{
私人模拟商店;
公共Moquser商店()
:base(新Mock().Object,新Mock().Object,null)
{
_store=new Mock(new Mock().Object,new Mock().Object,null);
_store.Setup(x=>x.FindByIdAsync(“realuser”,默认值(CancellationToken))).Returns(Task.Run(()=>newidentityuser(“realuser”)));
_Setup(x=>x.FindByIdAsync(“notrealuser”,默认值(CancellationToken)).Throws(新的CustomIdentityNotFoundException());
_store.Setup(x=>x.CreateAsync(新的IdentityUser(“realuser”),默认值(CancellationToken)).Returns(Task.Run(()=>IdentityResult.Success));
}
public IdentityUserStore存储{get=>\u Store.Object;}
}
调用MoqUserManager
构造函数时,我得到引用未设置为对象实例的错误
我的问题是:对于依赖于UserManager
和/或SignInManager
的这些类型的控制器,单元测试的最佳实践是什么(我会满足于工作,但很讨厌),什么是模拟UserStore
依赖关系的容易重复的方法?我考虑了DI模型和控制器的依赖关系。我只需要UserManager
中的一些方法,因此我从理论上考虑从userscoontroller
中删除对UserManager
的依赖,并将其替换为实现与UserManager
中所需签名相同的接口。让我们调用该接口IMYUserManager
:
public interface IMYUserManager
{
Task<IdentityUser> FindByIdAsync(string uniqueid);
Task<IdentityResult> CreateAsync(IdentityUser IdentityUser);
Task<IdentityResult> UpdateAsync(IdentityUser IdentityUser);
Task<IdentityResult> DeleteAsync(IdentityUser result);
}
public class MYUserManager : UserManager<IdentityUser>, IMYUserManager
{
public MYUserManager(IUserStore<IdentityUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<IdentityUser> passwordHasher, IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<IdentityUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public override Task<IdentityUser> FindByIdAsync(string userId)
{
return base.FindByIdAsync(userId);
}
//Removed other overridden methods for brevity; They also call the base class method
}
快到家了。接下来,我自然更新了userscoontroller
以使用IMYUserManager
界面:
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private ILogger<UsersController> _logger;
private readonly IMYUserManager _usermanager;
public UsersController(ILogger<UsersController> logger, IMYUserManager
usermanager)
{
_usermanager = usermanager;
_logger = logger;
}
}
[路由(“api/[控制器]”)]
[ApiController]
公共类UsersController:ControllerBase
{
私人ILogger_记录器;
私有只读IMYUserManager\u usermanager;
公共用户控制器(ILogger记录器、IMYUserManager
用户管理器)
{
_usermanager=usermanager;
_记录器=记录器;
}
}
当然,在这之后,我必须使服务容器能够为所有想要享用的人提供这种依赖性:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMYUserManager, MYUserManager>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void配置服务(IServiceCollection服务)
{
services.addScope();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
最后,在验证了实际构建之后,我更新了测试类:
public class UsersControllerTests
{
public UsersController _usersController;
private Mock<IMYUserManager> _userManager;
public UsersControllerTests()
{
_userManager = new Mock<IMYUserManager>();
_usersController = new UsersController((new Mock<ILogger<UsersController>>
()).Object, _userManager.Object);
}
[Fact]
public async Task GetUser_ReturnsOkObjectResult_WhenModelStateIsValid()
{
//Setup
_userManager.Setup(x => x.FindByIdAsync("realuser"))
.Returns(Task.Run(() => new IdentityUser("realuser","realuser1")));
_usersController.ModelState.Clear();
//Test
ObjectResult response = await _usersController.GetUser("realuser");
//Assert
//Should receive 200 and user data content body
response.StatusCode.Should().Be((int)System.Net.HttpStatusCode.OK);
response.Value.Should().NotBeNull();
}
}
公共类用户控件测试
{
公共用户控制器\u用户控制器;
私人模拟用户管理器;
公共用户controllertests()
{
_userManager=newmock();
_usersController=新usersController((新模拟
()).Object,_userManager.Object);
}
[事实]
公共异步任务GetUser\u ReturnsOkObjectResult\u WhenModelStateIsValid()
{
//设置
_userManager.Setup(x=>x.FindByIdAsync(“realuser”))
.Returns(Task.Run(()=>newidentityuser(“realuser”,“realuser1”)));
_usersController.ModelState.Clear();
//试验
ObjectResult response=wait_usersController.GetUser(“realuser”);
//断言
//应接收200和用户数据内容体
response.StatusCode.Should().Be((int)System.Net.HttpStatusCode.OK);
response.Value.Should().NotBeNull();
}
}
是什么让这成为一个好的解决方案?
有几件事:
从