我如何在测试派生时模拟我的基本方法 class
How can i mock i base method when test derived class
我有一个问题,我想在我的用户服务中编写自定义函数的单元测试。但此用户服务派生自 asp.net 核心身份用户管理器。我想扩展用户管理器的功能,因此我从它派生。
所以我想测试方法 CreateCRohMUserAsync
public class UserService : UserManager<User>, IUserService
{
public UserService(IUserStore<User> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<User> passwordHasher,
IEnumerable<IUserValidator<User>> userValidators,
IEnumerable<IPasswordValidator<User>> passwordValidators,
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<User>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public async Task<IdentityResult> CreateCRohMUserAsync(User user)
{
PasswordGenerator pwGenerator = new PasswordGenerator();
var password = pwGenerator.Generate();
user.UserName = GetUniqueUserName(user.FirstName, user.LastName);
var result = await base.CreateAsync(user, password);
if (result.Succeeded)
{
//TODO: send mail to created user
}
return result;
}
public string GetUniqueUserName(string firstName, string lastName)
{
if (string.IsNullOrEmpty(firstName))
{
throw new ArgumentNullException(nameof(firstName), "firstName can not be null");
}
if (string.IsNullOrEmpty(lastName))
{
throw new ArgumentNullException(nameof(lastName), "lastName can not be null");
}
return lastName + firstName.Substring(0, 2) + Users.Count() + 1;
}
}
}
在我的单元测试中,我想模拟 base.CreateAsync(user,password) 和 Users.Count() 我的基地的功能 class。但我不知道该怎么做。
还是我的实现有误?我如何对这个方法进行单元测试?
根据您的示例代码,我建议您将继承更改为组合,因为您只使用基础方法 class,而不是覆盖任何方法。
interface IUserManager
{
IdentityResult CreateAsync();
}
class DefaultUserManager : IUserManager
{
public DefaultUserManager(UserManager<User> manager)
{
}
public IdentityResult CreateAsync()
{
// Call UserManager<User>
}
}
public class UserService : IUserService
{
private readonly IUserManager _userManager;
public UserService(IUserManager userManager, ...)
{
_userManager = userManager;
}
public async Task<IdentityResult> CreateCRohMUserAsync(User user)
{
var result = await _userManager.CreateAsync();
return result;
}
}
我会将 UserManager<User>
的使用抽象到一个名为 IUserManager
的接口中,实现仍然使用 UserManager<User>
。但是,您的 UserService
会调用该接口,因此在您的单元测试中,您可以简单地提供 IUserManager
的模拟实例并避免调用 UserManager<User>
.
假设您正在使用 moq。
首先,您必须删除 CreateCRohMUserAsync
中的 base
关键字:
var result = await CreateAsync(user, password); // Previously base.CreateAsync
如果它存在,基 class 的实现总是被调用 - 模拟 CreateAsync
将没有效果(最小起订量通过覆盖它们来模拟方法)。
删除 base
后,您可以像这样测试 CreateCRohMUserAsync
:
[Fact]
public async void CreateCRohMUserAsync_DoesSomething()
{
// Arrange
...
Mock<UserService> mockTestSubject = new Mock<UserService>(...); // Pass services you'd normally pass to the UserService constructor
mockTestSubject.CallBase = true; // So when you call CreateCRohMUserAsync, the logic you defined runs
mockTestSubject.Setup(t => t.CreateAsync(...)).ReturnsAsync(...); // Mock CreateAsync
// If GetUniqueUserName is virtual, you can mock it too
// mockTestSubject.Setup(t => t.GetUniqueUserName(...)).Returns(...);
// Act
IdentityResult result = await mockTestSubject.Object.CreateCRohMUserAsync(...);
// Assert
...
}
同样的模式也适用于 GetUniqueUserName
。
本质上:
- 模拟你的派生 class,这样你就可以设置你的基础 class 的虚拟成员。
- 将
CallBase
设置为真,这样您就可以 运行 您在派生 class 中定义的逻辑。
我有一个问题,我想在我的用户服务中编写自定义函数的单元测试。但此用户服务派生自 asp.net 核心身份用户管理器。我想扩展用户管理器的功能,因此我从它派生。 所以我想测试方法 CreateCRohMUserAsync
public class UserService : UserManager<User>, IUserService
{
public UserService(IUserStore<User> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<User> passwordHasher,
IEnumerable<IUserValidator<User>> userValidators,
IEnumerable<IPasswordValidator<User>> passwordValidators,
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<User>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public async Task<IdentityResult> CreateCRohMUserAsync(User user)
{
PasswordGenerator pwGenerator = new PasswordGenerator();
var password = pwGenerator.Generate();
user.UserName = GetUniqueUserName(user.FirstName, user.LastName);
var result = await base.CreateAsync(user, password);
if (result.Succeeded)
{
//TODO: send mail to created user
}
return result;
}
public string GetUniqueUserName(string firstName, string lastName)
{
if (string.IsNullOrEmpty(firstName))
{
throw new ArgumentNullException(nameof(firstName), "firstName can not be null");
}
if (string.IsNullOrEmpty(lastName))
{
throw new ArgumentNullException(nameof(lastName), "lastName can not be null");
}
return lastName + firstName.Substring(0, 2) + Users.Count() + 1;
}
}
}
在我的单元测试中,我想模拟 base.CreateAsync(user,password) 和 Users.Count() 我的基地的功能 class。但我不知道该怎么做。 还是我的实现有误?我如何对这个方法进行单元测试?
根据您的示例代码,我建议您将继承更改为组合,因为您只使用基础方法 class,而不是覆盖任何方法。
interface IUserManager
{
IdentityResult CreateAsync();
}
class DefaultUserManager : IUserManager
{
public DefaultUserManager(UserManager<User> manager)
{
}
public IdentityResult CreateAsync()
{
// Call UserManager<User>
}
}
public class UserService : IUserService
{
private readonly IUserManager _userManager;
public UserService(IUserManager userManager, ...)
{
_userManager = userManager;
}
public async Task<IdentityResult> CreateCRohMUserAsync(User user)
{
var result = await _userManager.CreateAsync();
return result;
}
}
我会将 UserManager<User>
的使用抽象到一个名为 IUserManager
的接口中,实现仍然使用 UserManager<User>
。但是,您的 UserService
会调用该接口,因此在您的单元测试中,您可以简单地提供 IUserManager
的模拟实例并避免调用 UserManager<User>
.
假设您正在使用 moq。
首先,您必须删除 CreateCRohMUserAsync
中的 base
关键字:
var result = await CreateAsync(user, password); // Previously base.CreateAsync
如果它存在,基 class 的实现总是被调用 - 模拟 CreateAsync
将没有效果(最小起订量通过覆盖它们来模拟方法)。
删除 base
后,您可以像这样测试 CreateCRohMUserAsync
:
[Fact]
public async void CreateCRohMUserAsync_DoesSomething()
{
// Arrange
...
Mock<UserService> mockTestSubject = new Mock<UserService>(...); // Pass services you'd normally pass to the UserService constructor
mockTestSubject.CallBase = true; // So when you call CreateCRohMUserAsync, the logic you defined runs
mockTestSubject.Setup(t => t.CreateAsync(...)).ReturnsAsync(...); // Mock CreateAsync
// If GetUniqueUserName is virtual, you can mock it too
// mockTestSubject.Setup(t => t.GetUniqueUserName(...)).Returns(...);
// Act
IdentityResult result = await mockTestSubject.Object.CreateCRohMUserAsync(...);
// Assert
...
}
同样的模式也适用于 GetUniqueUserName
。
本质上:
- 模拟你的派生 class,这样你就可以设置你的基础 class 的虚拟成员。
- 将
CallBase
设置为真,这样您就可以 运行 您在派生 class 中定义的逻辑。