我试图在AppService的XUnit测试中实现依赖注入。理想的目标是运行原始的应用程序启动/配置,并使用启动中的任何依赖注入,而不是在我的测试中重新初始化所有的DI,这就是整个目标。
更新:莫森的答案很接近。需要更新两个语法/需求错误才能正常工作。
由于某些原因,原始应用程序可以工作,并可以调用部门应用程序服务。然而,它无法调用Xunit。最后,使用原始应用程序的启动和配置使Testserver正常工作。现在接收错误如下:
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
需要在测试中使用依赖注入,就像真正的应用程序一样。最初的应用程序就是这样做的。下面的答案目前还不够,一个答案使用的是非当前目标的嘲弄,另一个答案使用的是绕过问题目的的控制器。
注意:IDepartmentAppService依赖于IDepartmentRepository,后者也被注入到Startup类中,并依赖于Automapper。这就是为什么给整个创业班打电话。
好资源:
如何对asp进行单元测试。net核心应用程序,带有构造函数依赖项注入
XUnit项目中的依赖注入
当你在测试的时候。你需要使用mocking库或者直接在constructor ie上注入你的服务。
public DBContext context;
public IDepartmentAppService departmentAppService;
/// Inject DepartmentAppService here
public DepartmentAppServiceTest(DepartmentAppService departmentAppService)
{
this.departmentAppService = departmentAppService;
}
使用自定义Web应用程序工厂和ServiceProvider。GetRequiredService
下面,请随意编辑和优化答案
定制Web应用程序工厂:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) =>
{
var type = typeof(TStartup);
var path = @"C:\\OriginalApplication";
configurationBuilder.AddJsonFile($"{path}\\appsettings.json", optional: true, reloadOnChange: true);
configurationBuilder.AddEnvironmentVariables();
});
// if you want to override Physical database with in-memory database
builder.ConfigureServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<ApplicationDBContext>(options =>
{
options.UseInMemoryDatabase("DBInMemoryTest");
options.UseInternalServiceProvider(serviceProvider);
});
});
}
}
集成测试:
public class DepartmentAppServiceTest : IClassFixture<CustomWebApplicationFactory<OriginalApplication.Startup>>
{
public CustomWebApplicationFactory<OriginalApplication.Startup> _factory;
public DepartmentAppServiceTest(CustomWebApplicationFactory<OriginalApplication.Startup> factory)
{
_factory = factory;
_factory.CreateClient();
}
[Fact]
public async Task ValidateDepartmentAppService()
{
using (var scope = _factory.Server.Host.Services.CreateScope())
{
var departmentAppService = scope.ServiceProvider.GetRequiredService<IDepartmentAppService>();
var dbtest = scope.ServiceProvider.GetRequiredService<ApplicationDBContext>();
dbtest.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
dbtest.SaveChanges();
var departmentDto = await departmentAppService.GetDepartmentById(2);
Assert.Equal("123", departmentDto.DepartmentCode);
}
}
}
资源:
https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
https://fullstackmark.com/post/20/painless-integration-testing-with-aspnet-core-web-api
您正在混合单元测试和集成测试TestServer
用于集成测试,如果要重用Startup
类以避免再次注册依赖项,则应使用HttpClient
并对使用IDepartmentAppService
的控制器和操作进行HTTP调用。
如果你想进行单元测试,你需要设置DI并注册所有需要的依赖项来测试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配置包装在另一个类中,并在任何你想要的地方使用那个类。
通过启动使用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
类:
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
类中的配置服务
:
public IServiceCollection ConfigureServices(IServiceCollection services)
{
.
.
.
return services;
在测试项目中,重用IocConfig
或Startup
类:
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核心库项目中将一个类注入另一个类?在API项目中的StartUp类ConfigureServices中,我应该在哪里配置DI?
我有一个ASP。NET核心2.1站点,注册了几个DbContext和一些条件连接字符串。 出于调试目的,我想列出所有注册的<code>DbContext</code>及其连接字符串,以确保所有内容都配置良好。 这只是一个“测试”功能,不用于任何其他用途,因此请不要回答“您不应该这样做”、“它是反模式”等。
复合在这种情况下“消耗”一个子对象数组,从某种意义上说,“吸入”用密钥注册的IFoo的每个实现。这是一个重要的方面:如果您要用一个键注册复合,它将尝试实例化自己,并立即导致StackOverflow异常。 本机DI不支持这些命名注册。 这个例子是从这里提取的
我的大多数服务通常与crud方法相同
我已经为主项目和测试创建了.NET Standard1.6类库,并复制了代码。我将单元测试改为使用xUnit属性和断言,而不是NUnit。 除此之外,我基本上遵循了文档中的说明,因此添加了以下NuGet包: microsoft.net.test.sdk xUnit xunit.runner.visualstudio
我有一个.NET core项目,并且正在尝试使用授权选项创建自定义策略,如此处的文档所示: ASP.NET.Core授权-需求处理程序中的依赖注入 这些示例显示了使用 1 个参数(一个简单的 int 值)设置授权要求。我的自定义要求需要一个字符串参数以及一个 DbContext 对象。我想在运行时将 DbContext 注入到需求的构造函数中。我正在使用 Autofac 容器。我不确定如何实现这一