如何通过模拟IUserService对我的reportcontroller进行单元测试

时间:2022-04-02 07:32:22

I have a controller:

我有一个控制器:

private readonly IUserService _userService;

    public ReportController ( IOptions<AppSettings> appSettings,
                              UserManager<ApplicationUser> userManager,
                              IUserService userService ) : base( appSettings, userManager ) {
        _userService = userService;
    }

    public async Task<ActionResult> Report ( string path ) {
        var currentUser = await GetCurrentUserAsync();
        var excludedItems = _userService.GetUserExcludedReportsById( currentUser.Id ).Select( er => er.Path );

        if ( string.IsNullOrEmpty( path ) || excludedItems.Any( path.Contains ) ) {
            return RedirectToAction( nameof(HomeController.Index), "Home" );
        }

        var customItems = _userService.GetUserCustomReportsById( currentUser.Id ).Select( er => er.Path );

        if ( path.Contains( AppSettings.CustomReportsFolderName ) && !customItems.Any( path.Contains ) ) {
            return RedirectToAction( nameof(HomeController.Index), "Home" );
        }


        var model = GetReportViewerModel( Request );
        model.Parameters.Clear();
        var dbname = _userService.GetDefaultDbName( (await GetCurrentUserAsync()).Id );
        model.Parameters.Add( "connectionStr", new[] {
            dbname
        } );
        model.ReportPath = path;
        model.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Ntlm;

        return View( "Report", model );
    }
}

Can you tell me how I approach unit testing this? My main issues are:

你能告诉我如何进行单元测试吗?我的主要问题是:

It has 3 dependencies in the constructor, I have managed to Mock them apart from the UserManager requirement, which seems to have a large amount of dependencies in itself. I have tried a suggestion from this site, as per below, but the UserManager object complains about missing parameters. I keep getting told It should be simple, but surely I need these to instantiate an instance of the controller?

它在构造函数中有3个依赖项,我已经设法将它们与UserManager需求区分开来,而UserManager需求本身似乎有很多依赖项。我已尝试过该网站的建议,如下所示,但UserManager对象抱怨缺少参数。我一直被告知它应该很简单,但我确实需要这些实例化控制器的实例?

In terms of what to test on the Report method, my thoughts are: The two IF statements return a redirect. Also the expected return is valid.

关于在Report方法上测试的内容,我的想法是:两个IF语句返回重定向。预期回报也是有效的。

So far I have:

到目前为止我有:

var mockUserService = new Mock<IUserService>();
        AppSettings appSettings = new AppSettings() { };
        IOptions<AppSettings> options = Options.Create(appSettings);
        var mockUserStore = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
        var userManager = new UserManager<ApplicationUser>(mockUserStore.Object);
        var mockDependancyResolver = new Mock<IDependencyResolver>();

        var sut = new ReportController(options, userManager.Object, mockUserService.Object);

1 个解决方案

#1


0  

I keep getting told It should be simple

我一直被告知它应该很简单

Usually it is simple when your controller depends on abstractions only (i.e. interfaces). Then you can just mock them using Mock class. But UserManager<> is not abstraction it is a concrete class that can't be mocked by Mock. But since most of methods of UserManager<> are virtual you can derive from it and override them:

通常,当您的控制器仅依赖于抽象(即接口)时,它很简单。然后你可以使用Mock类来模拟它们。但是UserManager <>不是抽象,它是一个不能被Mock嘲笑的具体类。但由于UserManager <>的大多数方法都是虚拟的,因此您可以从中获取并覆盖它们:

public class FakeUserManager : UserManager<ApplicationUser>
{
    public FakeUserManager()
        : base(new Mock<IUserStore<ApplicationUser>>().Object,
              new Mock<IOptions<IdentityOptions>>().Object,
              new Mock<IPasswordHasher<ApplicationUser>>().Object,
              new IUserValidator<ApplicationUser>[0],
              new IPasswordValidator<ApplicationUser>[0],
              new Mock<ILookupNormalizer>().Object,
              new Mock<IdentityErrorDescriber>().Object,
              new Mock<IServiceProvider>().Object,
              new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
    { }
    ...
    public override Task<ApplicationUser> FindByIdAsync(string id)
    {
        return Task.FromResult(new ApplicationUser { Id = id });
    }
    ...
}

then use it:

然后使用它:

var sut = new ReportController(options, new FakeUserManager(), mockUserService.Object);

#1


0  

I keep getting told It should be simple

我一直被告知它应该很简单

Usually it is simple when your controller depends on abstractions only (i.e. interfaces). Then you can just mock them using Mock class. But UserManager<> is not abstraction it is a concrete class that can't be mocked by Mock. But since most of methods of UserManager<> are virtual you can derive from it and override them:

通常,当您的控制器仅依赖于抽象(即接口)时,它很简单。然后你可以使用Mock类来模拟它们。但是UserManager <>不是抽象,它是一个不能被Mock嘲笑的具体类。但由于UserManager <>的大多数方法都是虚拟的,因此您可以从中获取并覆盖它们:

public class FakeUserManager : UserManager<ApplicationUser>
{
    public FakeUserManager()
        : base(new Mock<IUserStore<ApplicationUser>>().Object,
              new Mock<IOptions<IdentityOptions>>().Object,
              new Mock<IPasswordHasher<ApplicationUser>>().Object,
              new IUserValidator<ApplicationUser>[0],
              new IPasswordValidator<ApplicationUser>[0],
              new Mock<ILookupNormalizer>().Object,
              new Mock<IdentityErrorDescriber>().Object,
              new Mock<IServiceProvider>().Object,
              new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
    { }
    ...
    public override Task<ApplicationUser> FindByIdAsync(string id)
    {
        return Task.FromResult(new ApplicationUser { Id = id });
    }
    ...
}

then use it:

然后使用它:

var sut = new ReportController(options, new FakeUserManager(), mockUserService.Object);