如何使用构造函数依赖项注入对ASP.NET核心应用程序进行单元测试 [英] how to unit test asp.net core application with constructor dependency injection

查看:118
本文介绍了如何使用构造函数依赖项注入对ASP.NET核心应用程序进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个asp.net核心应用程序,该应用程序使用在应用程序的startup.cs类中定义的依赖项注入:

I have a asp.net core application that uses dependency injection defined in the startup.cs class of the application:

    public void ConfigureServices(IServiceCollection services)
    {

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration["Data:FotballConnection:DefaultConnection"]));


        // Repositories
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserRoleRepository, UserRoleRepository>();
        services.AddScoped<IRoleRepository, RoleRepository>();
        services.AddScoped<ILoggingRepository, LoggingRepository>();

        // Services
        services.AddScoped<IMembershipService, MembershipService>();
        services.AddScoped<IEncryptionService, EncryptionService>();

        // new repos
        services.AddScoped<IMatchService, MatchService>();
        services.AddScoped<IMatchRepository, MatchRepository>();
        services.AddScoped<IMatchBetRepository, MatchBetRepository>();
        services.AddScoped<ITeamRepository, TeamRepository>();

        services.AddScoped<IFootballAPI, FootballAPIService>();

这允许这样的事情:

[Route("api/[controller]")]
public class MatchController : AuthorizedController
{
    private readonly IMatchService _matchService;
    private readonly IMatchRepository _matchRepository;
    private readonly IMatchBetRepository _matchBetRepository;
    private readonly IUserRepository _userRepository;
    private readonly ILoggingRepository _loggingRepository;

    public MatchController(IMatchService matchService, IMatchRepository matchRepository, IMatchBetRepository matchBetRepository, ILoggingRepository loggingRepository, IUserRepository userRepository)
    {
        _matchService = matchService;
        _matchRepository = matchRepository;
        _matchBetRepository = matchBetRepository;
        _userRepository = userRepository;
        _loggingRepository = loggingRepository;
    }

这很整洁.但是当我要进行单元测试时成为一个问题.因为我的测试库没有在其中设置依赖项注入的startup.cs.因此,将这些接口作为参数的类将为null.

This is very neat. But becomes a problem when I want to unit test. Because my test library does not have a startup.cs where I setup dependency injection. So a class with these interfaces as params will just be null.

namespace TestLibrary
{
    public class FootballAPIService
    {
        private readonly IMatchRepository _matchRepository;
        private readonly ITeamRepository _teamRepository;

        public FootballAPIService(IMatchRepository matchRepository, ITeamRepository teamRepository)

        {
            _matchRepository = matchRepository;
            _teamRepository = teamRepository;

在上面的代码中,在测试库中, _matchRepository _teamRepository 将仅为 null . :(

In the code above, in the test library, _matchRepository and _teamRepository, will just be null. :(

我可以做类似ConfigureServices之类的事情,在我的测试库项目中定义依赖项注入吗?

Can I do something like ConfigureServices, where I define dependency injection in my test library project?

推荐答案

.net核心中的控制器从一开始就考虑了依赖项注入,但这并不意味着您需要使用依赖项注入容器.

Your controllers in .net core have dependency injection in mind from the start, but this does not mean you are required to use a dependency injection container.

给出一个更简单的类,例如:

Given a simpler class like:

public class MyController : Controller
{

    private readonly IMyInterface _myInterface;

    public MyController(IMyInterface myInterface)
    {
        _myInterface = myInterface;
    }

    public JsonResult Get()
    {
        return Json(_myInterface.Get());
    }
}

public interface IMyInterface
{
    IEnumerable<MyObject> Get();
}

public class MyClass : IMyInterface
{
    public IEnumerable<MyObject> Get()
    {
        // implementation
    }
}

因此,在您的应用程序中,您正在使用startup.cs中的依赖项注入容器,它只提供了MyClass的构想,以便在遇到IMyInterface时使用.但是,这并不意味着它是获取MyController实例的唯一方法.

So in your app, you're using the dependency injection container in your startup.cs, which does nothing more than provide a concretion of MyClass to use when IMyInterface is encountered. This does not mean it is the only way of getting instances of MyController however.

单元 测试场景中,您可以(并且应该)提供自己的IMyInterface实现(或模拟/存根/伪造),如下所示:

In a unit testing scenario, you can (and should) provide your own implementation (or mock/stub/fake) of IMyInterface as so:

public class MyTestClass : IMyInterface
{
    public IEnumerable<MyObject> Get()
    {
        List<MyObject> list = new List<MyObject>();
        // populate list
        return list;
    }        
}

并在您的测试中:

[TestClass]
public class MyControllerTests
{

    MyController _systemUnderTest;
    IMyInterface _myInterface;

    [TestInitialize]
    public void Setup()
    {
        _myInterface = new MyTestClass();
        _systemUnderTest = new MyController(_myInterface);
    }

}

因此对于单元测试MyController的范围,IMyInterface的实际实现无关紧要(并且 不应该 重要),只有接口本身才重要.我们已经通过MyTestClass提供了IMyInterface的伪"实现,但是您也可以通过MoqRhinoMocks这样的模拟来实现.

So for the scope of unit testing MyController, the actual implementation of IMyInterface does not matter (and should not matter), only the interface itself matters. We have provided a "fake" implementation of IMyInterface through MyTestClass, but you could also do this with a mock like through Moq or RhinoMocks.

最重要的是,您实际上不需要依赖项注入容器来完成测试,只需一个单独的,可控的,实现/模拟/存根/伪造的测试类依赖项即可.

Bottom line, you do not actually need the dependency injection container to accomplish your tests, only a separate, controllable, implementation/mock/stub/fake of your tested classes dependencies.

这篇关于如何使用构造函数依赖项注入对ASP.NET核心应用程序进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆