如何使用构造函数dependency injectionunit testingasp.net核心应用程序

我有一个asp.net核心应用程序,它使用在应用程序的startup.cs类中定义的dependency injection:

public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => options.UseSqlServer(Configuration["Data:FotballConnection:DefaultConnection"])); // Repositories services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); // Services services.AddScoped(); services.AddScoped(); // new repos services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); 

这允许这样的事情:

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

这很整洁。 但是当我想进行unit testing时会成为一个问题。 因为我的测试库没有启动dependency injection的startup.cs。 所以这些接口作为params的类只是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 。 🙁

我可以执行像ConfigureServices这样的操作,在我的测试库项目中定义dependency injection吗?

您的.net核心控制器从一开始就考虑了dependency injection,但这并不意味着您需要使用dependency injection容器。

给出一个更简单的类:

 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 Get(); } public class MyClass : IMyInterface { public IEnumerable Get() { // implementation } } 

所以在你的应用程序中,你在startup.cs使用了dependency injection容器, startup.cs提供了在遇到IMyInterface时使用的MyClass的具体结果。 这并不意味着它是获取MyController实例的唯一方法。

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

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

在你的测试中:

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

因此对于unit testingMyController的范围, MyController的实际实现并不重要(并且应该无关紧要),只有接口本身很重要。 我们通过MyTestClass提供了IMyInterface的“假”实现,但你也可以通过MoqRhinoMocks这样的模拟实现。

最重要的是,您实际上并不需要dependency injection容器来完成测试,只需要一个单独的,可控制的实现/模拟/存根/假的测试类依赖项。

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

 [TestClass] public class MatchRepositoryTests { private readonly IMatchRepository matchRepository; public MatchRepositoryTests() { var services = new ServiceCollection(); services.AddTransient(); var serviceProvider = services.BuildServiceProvider(); matchRepository = serviceProvider.GetService(); } } 

你为什么要在测试类中注入它们? 您通常会测试MatchController,例如,使用RhinoMocks之类的工具来创建存根或模拟。 以下是使用它和MSTest的示例,您可以从中推断出:

 [TestClass] public class MatchControllerTests { private readonly MatchController _sut; private readonly IMatchService _matchService; public MatchControllerTests() { _matchService = MockRepository.GenerateMock(); _sut = new ProductController(_matchService); } [TestMethod] public void DoSomething_WithCertainParameters_ShouldDoSomething() { _matchService .Expect(x => x.GetMatches(Arg.Is.Anything)) .Return(new []{new Match()}); _sut.DoSomething(); _matchService.AssertWasCalled(x => x.GetMatches(Arg.Is.Anything); } 

一个简单的方法,我写了一个通用依赖解析器帮助器类,然后在我的unit testing类中构建了IWebHost。

通用依赖性解析器

  public class DependencyResolverHelpercs { private readonly IWebHost _webHost; ///  public DependencyResolverHelpercs(IWebHost WebHost) => _webHost = WebHost; public T GetService() { using (var serviceScope = _webHost.Services.CreateScope()) { var services = serviceScope.ServiceProvider; try { var scopedService = services.GetRequiredService(); return scopedService; } catch (Exception e) { Console.WriteLine(e); throw; } }; } } } 

unit testing项目

  [TestFixture] public class DependencyResolverTests { private DependencyResolverHelpercs _serviceProvider; public DependencyResolverTests() { var webHost = WebHost.CreateDefaultBuilder() .UseStartup() .Build(); _serviceProvider = new DependencyResolverHelpercs(webHost); } [Test] public void Service_Should_Get_Resolved() { //Act var YourService = _serviceProvider.GetService(); //Assert Assert.IsNotNull(YourService); } }