当前位置: 首页 > 知识库问答 >
问题:

如何对asp进行单元测试。具有构造函数依赖项注入的net core应用程序

哈泰
2023-03-14

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

    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;
    }

这很整洁。但当我想进行单元测试时,这就成了一个问题。因为我的测试库没有启动。cs,其中我设置依赖项注入。因此,将这些接口作为参数的类将仅为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将为空。:(

我可以做一些类似配置服务的事情吗,在那里我在测试库项目中定义依赖注入?

共有3个答案

东门俊民
2023-03-14

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

给定一个更简单的类,如:

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实例的唯一方法。

在单元测试场景中,您可以(并且应该)提供自己的IMyInterface实现(或mock/stub/false),如下所示:

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的模拟来做到这一点。

总之,您实际上不需要依赖注入容器来完成测试,只需要一个单独的、可控制的、实现/模拟/存根/假的被测试类依赖项。

赵高雅
2023-03-14

一种简单的方法是,我编写了一个通用依赖解析程序助手类,然后在单元测试类中构建了IWebHost。

通用依赖关系解析程序

        using Microsoft.AspNetCore.Hosting;
        using Microsoft.AspNetCore.Mvc;
        using Microsoft.Extensions.Configuration;
        using Microsoft.Extensions.DependencyInjection;
        using Microsoft.Extensions.Hosting;
        public class DependencyResolverHelper
        {
            private readonly IWebHost _webHost;
    
            /// <inheritdoc />
            public DependencyResolverHelper(IWebHost webHost) => _webHost = webHost;
    
            public T GetService<T>()
            {
                var serviceScope = _webHost.Services.CreateScope();
                var services = serviceScope.ServiceProvider;
                try
                {
                  var scopedService = services.GetRequiredService<T>();
                  return scopedService;
                }
                catch (Exception e)
                {
                   Console.WriteLine(e);
                   throw;
                }
            }
        }
    }

单元测试项目:

      [TestFixture]
        public class DependencyResolverTests
        {
            private DependencyResolverHelper _serviceProvider;

            public DependencyResolverTests()
            {

                var webHost = WebHost.CreateDefaultBuilder()
                    .UseStartup<Startup>()
                    .Build();
                _serviceProvider = new DependencyResolverHelper(webHost);
            }
    
            [Test]
            public void Service_Should_Get_Resolved()
            {
                
                //Act
                var YourService = _serviceProvider.GetService<IYourService>();
    
                //Assert
                Assert.IsNotNull(YourService);
            }
    

        }
羊舌迪
2023-03-14

虽然@Kritner的答案是正确的,但为了代码完整性和更好的DI体验,我更喜欢以下内容:

[TestClass]
public class MatchRepositoryTests
{
    private readonly IMatchRepository matchRepository;

    public MatchRepositoryTests()
    {
        var services = new ServiceCollection();
        services.AddTransient<IMatchRepository, MatchRepositoryStub>();

        var serviceProvider = services.BuildServiceProvider();

        matchRepository = serviceProvider.GetService<IMatchRepository>();
    }
}
 类似资料:
  • 问题内容: 我有一堂课要添加单元测试。该类具有多个构造函数,这些构造函数采用不同的类型并将其转换为规范形式,然后可以将其转换为其他类型。 实际上,它接受并转换为其他两种类型。 我正在尝试找出最合适的方法来测试这些构造函数。 应该有一个针对每个构造函数的测试和输出类型: 这导致许多不同的测试。如您所见,我正在努力命名它们。 应该有多个断言: 这有多个断言,这使我感到不舒服。它还正在测试getStri

  • 好的,到目前为止还好。但是等等,突然A需要额外的输入,比如一个对它的构造至关重要的名为“amount”的整数。现在,我的A构造函数需要如下所示: 突然这个新参数干扰了注射。此外,即使这确实有效,我也无法在从提供者检索新实例时输入“金额”,除非我弄错了。这里有几件事我可以做,我的问题是哪一件是最好的? 我可以通过添加一个方法来重构A,该方法应该在构造函数之后调用。然而,这是很难看的,因为它迫使我推迟

  • 问题内容: 在对Angular工厂进行单元测试(使用Karma + Jasmine)时,如何将存根依赖项注入到要测试的工厂中? 这是我的工厂: 实例化我的工厂时需要。 这是我的测试: 注意:我知道这允许用于控制器,但是我没有看到与之等效的工厂。 问题答案: 我知道有两种方法可以完成这样的事情: 使用和匿名模块注入模拟。 注入您要模拟的服务,并使用茉莉的间谍功能提供模拟值。 第二个选项仅在您确切知道

  • 在我的ASP. NET Core应用程序中,我有一个具有多个构造函数的标记助手。当ASP. NET 5尝试解析类型时,这会在运行时导致以下错误: 在类型'MyNameSpace中发现了多个接受所有给定参数类型的构造函数。MyTagHelper'.应该只有一个适用的构造函数。 其中一个构造函数是无参数的,另一个有一些参数不是注册类型的参数。我希望它使用无参数构造函数。 有没有办法让ASP. NET

  • 和另一个类: 在这种情况下,服务将按照预期注入到A类中。据我所知,你不能在a类中使用构造函数,否则注入将无法工作。有没有一种方法可以在a类中使用一个构造函数,同时注入服务?即: 在这种情况下,我得到的服务是空的,无论如何要解决这个问题?我希望能够从类型A中使用“new”创建一个对象,并注入服务,这可能吗?

  • 我正试图在计算机上实现这个例子https://developer.android.com/jetpack/docs/guide.这就解释了Android应用程序的结构。 当我使用相同的代码时,我会出现以下错误。 我可以找出这个错误与 当我为ViewModel编写默认的零输入构造函数时,我得到了以下错误。 我想不出这个错误的原因和解决方法。