C# 在测试派生类时,如何模拟i基方法

C# 在测试派生类时,如何模拟i基方法,c#,unit-testing,.net-core,asp.net-identity,C#,Unit Testing,.net Core,Asp.net Identity,我有一个问题,我想在我的用户服务中编写一个自定义函数的单元测试。但此用户服务源自asp.net核心标识用户管理器。我想扩展用户管理器的功能,因此我从中派生。 所以我想测试CreateCRohMUserAsync方法 公共类UserService:UserManager,IUserService { 公共用户服务(IUserStore存储, IOPS选项访问器, IPasswordHasher密码hasher, IEnumerable用户验证程序, IEnumerable密码验证器, ILooku

我有一个问题,我想在我的用户服务中编写一个自定义函数的单元测试。但此用户服务源自asp.net核心标识用户管理器。我想扩展用户管理器的功能,因此我从中派生。 所以我想测试CreateCRohMUserAsync方法

公共类UserService:UserManager,IUserService
{
公共用户服务(IUserStore存储,
IOPS选项访问器,
IPasswordHasher密码hasher,
IEnumerable用户验证程序,
IEnumerable密码验证器,
ILookupNormalizer-keyNormalizer、IdentityerRorDescriptiber错误、IServiceProvider服务、ILogger记录器):基本(存储、选项访问器、密码哈希器、用户验证器、密码验证器、keyNormalizer、错误、服务、记录器)
{
}
公共异步任务CreateCRohMUserAsync(用户)
{
PasswordGenerator pwGenerator=新的PasswordGenerator();
var password=pwGenerator.Generate();
user.UserName=GetUniqueUserName(user.FirstName,user.LastName);
var result=await base.CreateAsync(用户,密码);
if(result.successed)
{
//TODO:向创建的用户发送邮件
}
返回结果;
}
公共字符串GetUniqueUserName(字符串firstName,字符串lastName)
{
if(string.IsNullOrEmpty(firstName))
{
抛出新ArgumentNullException(nameof(firstName),“firstName不能为null”);
}
if(string.IsNullOrEmpty(lastName))
{
抛出新ArgumentNullException(nameof(lastName),“lastName不能为null”);
}
返回lastName+firstName.Substring(0,2)+Users.Count()+1;
}
}
}

在单元测试中,我想模拟基类的base.CreateAsync(user,password)和Users.Count()功能。但我不知道怎么做。
还是我的实现错了?如何对这种方法进行单元测试?

根据您的示例代码,我建议您将继承更改为组合,因为您只使用基类中的方法,而不重写任何方法

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;
    }
}
界面管理器 { IdentityResult CreateAsync(); } 类DefaultUserManager:IUserManager { 公共默认用户管理器(用户管理器) { } 公共标识Result CreateAsync() { //呼叫用户管理器 } } 公共类UserService:IUserService { 专用只读IUserManager\u userManager; 公共用户服务(IUserManager用户管理器,…) { _userManager=userManager; } 公共异步任务CreateCRohMUserAsync(用户) { var result=await_userManager.CreateAsync(); 返回结果; } }
我将
UserManager
的使用抽象为一个名为
IUserManager
的接口,而实现仍然使用
UserManager
。然而,您的
UserService
调用接口,因此在单元测试中,您可以简单地提供
IUserManager
的模拟实例,并避免调用
UserManager
,根据您的示例代码,我建议您将继承更改为composition,因为您只使用基类中的方法,不覆盖任何

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;
    }
}
界面管理器 { IdentityResult CreateAsync(); } 类DefaultUserManager:IUserManager { 公共默认用户管理器(用户管理器) { } 公共标识Result CreateAsync() { //呼叫用户管理器 } } 公共类UserService:IUserService { 专用只读IUserManager\u userManager; 公共用户服务(IUserManager用户管理器,…) { _userManager=userManager; } 公共异步任务CreateCRohMUserAsync(用户) { var result=await_userManager.CreateAsync(); 返回结果; } } 我将
UserManager
的使用抽象为一个名为
IUserManager
的接口,而实现仍然使用
UserManager
。然而,您的
UserService
调用接口,因此在单元测试中,您可以简单地提供一个
IUserManager
的模拟实例,并避免调用您正在使用的
UserManager

首先,您必须删除
CreateCRohMUserAsync
中的
base
关键字:

var result = await CreateAsync(user, password); // Previously base.CreateAsync
如果存在,基类的实现总是被调用-mock
CreateAsync
将无效(moq通过重写方法来模拟方法)

删除
base
后,可以像这样测试
CreateCRohMUserAsync

[事实]
public async void CreateCRohMUserAsync_DoesMething()
{
//安排
...
Mock mockTestSubject=new Mock(…);//将通常传递给UserService构造函数的服务传递给
mockTestSubject.CallBase=true;//因此,当您调用CreateCRohMUserAsync时,您定义的逻辑将运行
mockTestSubject.Setup(t=>t.CreateAsync(…).ReturnsAsync(…);//Mock CreateAsync
//如果GetUniqueUserName是虚拟的,您也可以模拟它
//mockTestSubject.Setup(t=>t.GetUniqueUserName(…)。返回(…);
//表演
IdentityResult结果=等待mockTestSubject.Object.CreateCRohMUserAsync(…);
//断言
...
}
同样的模式也适用于
GetUniqueUserName

基本上:

  • 模拟派生类,以便可以设置基类的虚拟成员
  • CallBase
    设置为true,以便可以运行在派生类中定义的逻辑
    • 您正在使用的ASUMING

      首先,您必须删除
      CreateCRohMUserAsync
      中的
      base
      关键字:

      var result = await CreateAsync(user, password); // Previously base.CreateAsync
      
      如果存在,基类的实现总是被调用-mock
      CreateAsync
      将无效(moq通过重写方法来模拟方法)

      移除
      base
      后,可以测试
      C