Net Core:在 Xunit 测试中为 AppService、Repository 等执行所有依赖注入 [英] Net Core: Execute All Dependency Injection in Xunit Test for AppService, Repository, etc
问题描述
我正在尝试在 AppService 的 Xunit 测试中实现依赖注入.理想的目标是运行原始应用程序启动/配置,并使用启动中的任何依赖注入,而不是在我的测试中再次重新初始化所有 DI,这就是有问题的整个目标.
I am trying to implement Dependency Injection in Xunit test for AppService. Ideal goal is to run the original application program Startup/configuration, and use any dependency injection that was in Startup, instead of reinitializing all the DI again in my test, thats the whole Goal in question.
更新:Mohsen 的回答很接近.需要更新几个语法/要求错误才能工作.
Update: Mohsen's answer is close. Need to update couple syntax/requirement errors to work.
由于某种原因,原始应用程序可以运行并且可以调用部门应用服务.但是,它不能在 Xunit 中调用.最终使用来自原始应用程序的启动和配置使 Testserver 工作.现在收到以下错误:
For some reason, original application works and can call Department App Service. However, it cannot call in Xunit. Finally got Testserver working using Startup and Configuration from original application. Now receiving error below:
Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService
namespace Testing.IntegrationTests
{
public class DepartmentAppServiceTest
{
public DBContext context;
public IDepartmentAppService departmentAppService;
public DepartmentAppServiceTest(IDepartmentAppService departmentAppService)
{
this.departmentAppService = departmentAppService;
}
[Fact]
public async Task Get_DepartmentById_Are_Equal()
{
var options = new DbContextOptionsBuilder<SharedServicesContext>()
.UseInMemoryDatabase(databaseName: "TestDatabase")
.Options;
context = new DBContext(options);
TestServer _server = new TestServer(new WebHostBuilder()
.UseContentRoot("C:\OriginalApplication")
.UseEnvironment("Development")
.UseConfiguration(new ConfigurationBuilder()
.SetBasePath("C:\OriginalApplication")
.AddJsonFile("appsettings.json")
.Build()).UseStartup<Startup>());
context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
context.SaveChanges();
var departmentDto = await departmentAppService.GetDepartmentById(2);
Assert.Equal("123", departmentDto.DepartmentCode);
}
}
}
我收到此错误:
Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService
在测试中需要像真正的应用程序一样使用依赖注入.原始应用程序执行此操作.下面的答案目前还不够,一个使用不是当前目标的模拟,其他答案使用绕过问题目的的控制器.
Need to use Dependency injection in testing just like real application. Original application does this. Answers below are not currently sufficient , one uses mocking which is not current goal, other answer uses Controller which bypass question purpose.
注意:IDepartmentAppService 依赖于 IDepartmentRepository,IDepartmentRepository 也注入到 Startup 类中,以及 Automapper 依赖项.这就是调用整个启动类的原因.
Note: IDepartmentAppService has dependency on IDepartmentRepository which is also injected in Startup class, and Automapper dependencies. This is why calling the whole startup class.
好的资源:
如何对asp进行单元测试具有构造函数依赖注入的 .net 核心应用程序
推荐答案
您正在混合单元测试和集成测试.TestServer
用于集成测试,如果您想重用 Startup
类以避免再次注册依赖项,您应该使用 HttpClient
并对控制器进行 HTTP 调用和使用 IDepartmentAppService
的操作.
You are mixing unit test with integration test. TestServer
is for integration test and if you want to reuse Startup
class to avoid register dependencies again, you should use HttpClient
and make HTTP call to controller and action that use IDepartmentAppService
.
如果你想做单元测试,你需要设置DI并注册所有需要的依赖来测试IDepartmentAppService
.
If you want do unit test, you need to setup DI and register all needed dependencies to test IDepartmentAppService
.
通过测试夹具使用 DI:
public class DependencySetupFixture
{
public DependencySetupFixture()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDbContext<SharedServicesContext>(options => options.UseInMemoryDatabase(databaseName: "TestDatabase"));
serviceCollection.AddTransient<IDepartmentRepository, DepartmentRepository>();
serviceCollection.AddTransient<IDepartmentAppService, DepartmentAppService>();
ServiceProvider = serviceCollection.BuildServiceProvider();
}
public ServiceProvider ServiceProvider { get; private set; }
}
public class DepartmentAppServiceTest : IClassFixture<DependencySetupFixture>
{
private ServiceProvider _serviceProvide;
public DepartmentAppServiceTest(DependencySetupFixture fixture)
{
_serviceProvide = fixture.ServiceProvider;
}
[Fact]
public async Task Get_DepartmentById_Are_Equal()
{
using(var scope = _serviceProvider.CreateScope())
{
// Arrange
var context = scope.ServiceProvider.GetServices<SharedServicesContext>();
context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
context.SaveChanges();
var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();
// Act
var departmentDto = await departmentAppService.GetDepartmentById(2);
// Arrange
Assert.Equal("123", departmentDto.DepartmentCode);
}
}
}
在单元测试中使用依赖注入并不是一个好主意,你应该避免这种情况.顺便说一句,如果您不想重复自己注册依赖项,您可以将 DI 配置包装在另一个类中,并在您想要的任何地方使用该类.
Using dependency injection with unit test is not good idea and you should avoid that. by the way if you want don't repeat your self for registering dependencies, you can wrap your DI configuration in another class and use that class anywhere you want.
通过 Startup.cs 使用 DI:
public class IocConfig
{
public static IServiceCollection Configure(IServiceCollection services, IConfiguration configuration)
{
serviceCollection
.AddDbContext<SomeContext>(options => options.UseSqlServer(configuration["ConnectionString"]));
serviceCollection.AddScoped<IDepartmentRepository, DepartmentRepository>();
serviceCollection.AddScoped<IDepartmentAppService, DepartmentAppService>();
.
.
.
return services;
}
}
在Startup
类和ConfigureServices
方法中只使用IocConfig
类:
in Startup
class and ConfigureServices
method just useIocConfig
class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
IocConfig.Configure(services, configuration);
services.AddMvc();
.
.
.
如果你不想使用IocConfig
类,在Startup
类中改变ConfigureServices
:
if you don't want use IocConfig
class, change ConfigureServices
in Startup
class:
public IServiceCollection ConfigureServices(IServiceCollection services)
{
.
.
.
return services;
并在测试项目中重用 IocConfig
或 Startup
类:
and in test project reuse IocConfig
or Startup
class:
public class DependencySetupFixture
{
public DependencySetupFixture()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true));
configuration = builder.Build();
var services = new ServiceCollection();
// services = IocConfig.Configure(services, configuration)
// or
// services = new Startup(configuration).ConfigureServices(services);
ServiceProvider = services.BuildServiceProvider();
}
public ServiceProvider ServiceProvider { get; private set; }
}
在测试方法中:
[Fact]
public async Task Get_DepartmentById_Are_Equal()
{
using (var scope = _serviceProvider.CreateScope())
{
// Arrange
var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();
// Act
var departmentDto = await departmentAppService.GetDepartmentById(2);
// Arrange
Assert.Equal("123", departmentDto.DepartmentCode);
}
}
这篇关于Net Core:在 Xunit 测试中为 AppService、Repository 等执行所有依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!