Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/solr/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Testing 通过重写的启动文件(.net core)执行XUnit DI_Testing_Asp.net Core_Xunit - Fatal编程技术网

Testing 通过重写的启动文件(.net core)执行XUnit DI

Testing 通过重写的启动文件(.net core)执行XUnit DI,testing,asp.net-core,xunit,Testing,Asp.net Core,Xunit,我已经构建了一个WebAPI,除了在Postman上运行的测试之外,我还想实现一些集成/单元测试 现在我的业务逻辑非常精简,大部分时间都是CRUD操作,因此我想从测试控制器开始 我有一个基本的设置。存储库模式(接口)、服务(业务逻辑)和控制器。 流程是控制器(DI服务)->服务(DI回购)->回购操作 因此,我所做的是重写我的启动文件,将其更改为内存中的数据库,其余的应该可以(我假设)添加服务,添加repo,现在我指向内存中的数据库,这对于我的基本测试来说是很好的 namespace API.U

我已经构建了一个WebAPI,除了在Postman上运行的测试之外,我还想实现一些集成/单元测试

现在我的业务逻辑非常精简,大部分时间都是CRUD操作,因此我想从测试控制器开始

我有一个基本的设置。存储库模式(接口)、服务(业务逻辑)和控制器。 流程是控制器(DI服务)->服务(DI回购)->回购操作

因此,我所做的是重写我的启动文件,将其更改为内存中的数据库,其余的应该可以(我假设)添加服务,添加repo,现在我指向内存中的数据库,这对于我的基本测试来说是很好的

namespace API.UnitTests
{    
    public class TestStartup : Startup
    {
        public TestStartup(IHostingEnvironment env)
            : base(env)
        {

        }

        public void ConfigureTestServices(IServiceCollection services)
        {
            base.ConfigureServices(services);
            //services.Replace<IService, IMockedService>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            base.Configure(app, env, loggerFactory);
        }

        public override void SetUpDataBase(IServiceCollection services)
        {
            var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
            var connectionString = connectionStringBuilder.ToString();
            var connection = new SqliteConnection(connectionString);

            services
                .AddEntityFrameworkSqlite()
                .AddDbContext<ApplicationDbContext>(
                    options => options.UseSqlite(connection)
                );
        }
    }
}

我错过了什么

不能在测试类上使用依赖项注入。您只能让xunit通过构造函数注入特殊的装置(请参阅)

对于集成测试,您希望使用Microsoft.AspNetCore.TestHost包中的
TestServer
类和单独的
Startup.cs
类(比继承imho更容易设置配置)

公共类TestStartup:Startup
{
公共测试启动(IHostingEnvironment环境)
{
var builder=new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(“appsettings.json”,可选:true,重载更改:true)
.AddJsonFile($“appsettings.{env.EnvironmentName}.json”,可选:true)
.AddenEnvironmentVariables();
Configuration=builder.Build();
}
公共IConfigurationRoot配置{get;}
public void ConfigureTestServices(IServiceCollection服务)
{
Replace(servicescriptor.Scoped());
services.AddEntityFrameworkSqlite()
.AddDbContext(
options=>options.UseSqlite(连接)
);
}
公共void配置(IApplicationBuilder应用程序)
{
//你通常在那里注册吗
}
}
在单元测试项目中,需要创建
TestServer
的实例并执行测试

公共类数据源控制器测试
{
专用只读测试服务器(TestServer);;
私有只读HttpClient\u客户端;
公共数据源控制器测试()
{
//安排
_服务器=新的TestServer(新的WebHostBuilder()
.UseStartup());
_client=_server.CreateClient();
}
[Xunit.理论,
InlineData(1)]
公共异步任务GetAll(int companyFk){
//表演
var response=await_client.GetAsync($“/api/datasource/{companyFk}”);
//rest服务的预期结果
应为var=@“[{”“数据”“:”“值1”“数据2”“:”“值2”“}]”;
//断言
//这样可以确保在出现4xx状态代码时返回成功http代码
//或异常(5xx代码),它会引发异常
response.EnsureSuccessStatusCode();
var resultString=await response.Content.ReadAsStringAsync();
等于(resultString,expectedString);
}
}
现在,当调用写入数据库的操作时,还可以检查数据是否确实写入数据库:

[Xunit.Theory,
InlineData(1)]
公共异步任务GetAll(int companyFk){
//表演
var response=await_client.deleteAncy($“/api/datasource/{companyFk}”);
//rest服务的预期结果
//断言
response.EnsureSuccessStatusCode();
//现在检查它是否真的存在于数据库中。为此,您需要一个实例
//TestServer有一个属性Host,它是一个IWebHost
//它有一个属性服务,即IoC容器
var provider=\u server.Host.Services;
var dbContext=provider.GetRequiredService();
var result=await dbContext.YourTable.Where(entity=>entity.Id==companyFk).Any();
//如果它被删除,查询结果应该是false
Assert.False(结果);
}
现在您可以在测试中使用

namespace Your.Test.Project
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IDependency, DependencyClass>();
        }
    }
}
和XUnit测试:

public class MyAwesomeTests
{
    private readonly IDependency _d;

    public MyAwesomeTests(IDependency d) => _d = d;

    [Fact]
    public void AssertThatWeDoStuff()
    {
        Assert.Equal(1, _d.Value);
    }
}

iirc您不能在测试类上使用依赖项注入。您只能让xunit通过构造函数注入特殊的fixture(请参阅类和集合fixture)。对于集成测试,您需要获取IServiceProvider的实例,并让它解析您的服务。对于控制器测试,您必须使用
TestServer
class=>docs.microsoft.com/en-us/aspnet/core/testing/integration-testingAlso,因此您可能不希望从Startup.cs继承,而是使用单独的类。使用引导代码。重写在某些配置中不起作用(例如,当您需要在A之后但在B之前执行代码时)。在调用
GetRequiredService
时,两次注册同一接口可能会导致异常,因为注册了多个接口(除非因此使用
services.TryAddXxx()
“如果测试类需要访问fixture实例,请将其作为构造函数参数添加,它将自动提供。”因此fixture可以使用DI,但不能从启动时开始DI?这太可惜了。当然,注册两次等也是意料之中的事。但是,如果我可以在测试中通过启动进行DI,那么我的测试就会启动它不是真正的IoC容器。xunit只是扫描test unity库中给定的类,并检查它是否实现了给定的类。但只有当类实现了marker接口
IClassFixture
ICollectionFixture
时,构造函数中唯一允许的参数才是类型
T
我的意思是你需要像
var\u datasourceService=provider.getRequiredService();Assert.NotEmpty(\u datasourceService.GetAll(companyFk));
你刚才还回答了我对我的问题的评论:)我将在今晚晚些时候尝试实现。非常感谢,这将非常有帮助!如何使
TestServer
使用m
public interface IDependency
{
    int Value { get; }
}

internal class DependencyClass : IDependency
{
    public int Value => 1;
}
public class MyAwesomeTests
{
    private readonly IDependency _d;

    public MyAwesomeTests(IDependency d) => _d = d;

    [Fact]
    public void AssertThatWeDoStuff()
    {
        Assert.Equal(1, _d.Value);
    }
}