Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.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
.net core 如何在.NETCore中编写真正的集成测试?_.net Core_Entity Framework Core_Tdd_Integration Testing_Asp.net Core Webapi - Fatal编程技术网

.net core 如何在.NETCore中编写真正的集成测试?

.net core 如何在.NETCore中编写真正的集成测试?,.net-core,entity-framework-core,tdd,integration-testing,asp.net-core-webapi,.net Core,Entity Framework Core,Tdd,Integration Testing,Asp.net Core Webapi,我有两个问题,但让我从头开始。我有ASP.NET Core 3.1 WebAPI,我正在寻找端到端测试控制器的最佳方法(我不想使用InMemoryprovider)。我将测试分为两组:“只读”(GET)和“读写”(POST,PUT,PATCH,DELETE)。对于只读测试,我希望创建一个全新的数据库(我使用代码优先迁移),然后逐个测试所有GET请求。对于读写请求,我希望创建新的数据库,并在测试后删除它 这就是我到目前为止所做的: public class TestFixture<TStar

我有两个问题,但让我从头开始。我有ASP.NET Core 3.1 WebAPI,我正在寻找端到端测试控制器的最佳方法(我不想使用
InMemory
provider
)。我将测试分为两组:“只读”(
GET
)和“读写”(
POST
PUT
PATCH
DELETE
)。对于只读测试,我希望创建一个全新的数据库(我使用代码优先迁移),然后逐个测试所有
GET
请求。对于读写请求,我希望创建新的数据库,并在测试后删除它

这就是我到目前为止所做的:

public class TestFixture<TStartup> : IDisposable where TStartup : class
{
    private readonly TestServer _server;

    public TestFixture()
    {
        var builder = new WebHostBuilder().UseStartup<TStartup>();
        builder.ConfigureAppConfiguration((context, conf) =>
        {
            conf.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly);
        });

        _server = new TestServer(builder);

        Client = _server.CreateClient();
        Client.BaseAddress = new Uri("http://localhost:5000");
    }

    public HttpClient Client { get; }

    public void Dispose()
    {
        Client.Dispose();
        _server.Dispose();
    }
}
公共类TestFixture:IDisposable其中TStartup:class
{
专用只读测试服务器(TestServer);;
公共测试设备()
{
var builder=new WebHostBuilder().UseStartup();
builder.ConfigureAppConfiguration((上下文,conf)=>
{
conf.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly);
});
_服务器=新的测试服务器(生成器);
Client=_server.CreateClient();
Client.BaseAddress=新Uri(“http://localhost:5000");
}
公共HttpClient客户端{get;}
公共空间处置()
{
Client.Dispose();
_Dispose();
}
}
。。。以及测试:

public class TestControllerShould : IClassFixture<TestFixture<Startup>>
{
    public HttpClient Client { get; }

    public TestControllerShould(TestFixture<Startup> fixture)
    {
        Client = fixture.Client;
    }

    [Fact]
    public async Task GetHelloWorld()
    {
        // Arrange
        var request = new HttpRequestMessage(new HttpMethod("GET"), "/test/");

        // Act
        var response = await Client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var content = await response.Content.ReadAsStringAsync();
        Assert.Equal("Hello World!", content);
    }
}
公共类TestControllerShould:IClassFixture
{
公共HttpClient客户端{get;}
公共测试控制器(测试夹具)
{
Client=fixture.Client;
}
[事实]
公共异步任务GetHelloWorld()
{
//安排
var request=newhttprequestmessage(newhttpmethod(“GET”),“/test/”;
//表演
var response=wait Client.sendaync(请求);
//断言
Assert.Equal(HttpStatusCode.OK,response.StatusCode);
var content=await response.content.ReadAsStringAsync();
Assert.Equal(“helloworld!”,content);
}
}
我有两个问题。首先,我使用生产连接字符串从主项目启动(这是需要的,因为我想测试它),所以这是一个问题。第二个问题,我想在每次“写”测试之后创建并删除数据库,但由于明显的原因,我不能这样做。因此,我的问题是:

  • 如何使用
    Startup
    类,但仅将数据库名称更改为
    “MyDatabaseName+Guid.NewGuid()”
  • 第二个问题,如何在每次测试前后创建和删除数据库(具有唯一名称) PS.我不想使用
    InMemory
    提供程序。我也不想在测试结束时使用事务和回滚。我想做真正的集成测试。

    如果我正确理解了ASP.NET Core 3.1的功能,我们现在需要使用
    WebApplicationFactory

    您可以覆盖其
    ConfigureWebHost
    方法来更改配置、依赖项等。我最近用一个假的数据库实现替换了一个真实的数据库实现:

    public class RestaurantApiFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            if (builder is null)
                throw new ArgumentNullException(nameof(builder));
    
            builder.ConfigureServices(services =>
            {
                var descriptors = services
                    .Where(d =>
                        d.ServiceType == typeof(IReservationsRepository))
                    .ToList();
                foreach (var d in descriptors)
                    services.Remove(d);
    
                services.AddSingleton<IReservationsRepository>(
                    new FakeDatabase());
            });
        }
    }
    
    看起来您正在使用xUnit.net。如果是这样,您可以使用其
    BeforeAfterestAttribute
    为每个测试创建和删除数据库

    我通常是这样做的:

    public class UseDatabaseAttribute : BeforeAfterTestAttribute
    {
        public override void Before(MethodInfo methodUnderTest)
        {
            using (var schemaStream = ReadSchema())
            using (var rdr = new StreamReader(schemaStream))
            {
                var schemaSql = rdr.ReadToEnd();
    
                var builder = new SqlConnectionStringBuilder(
                    ConnectionStrings.Reservations);
                builder.InitialCatalog = "Master";
                using (var conn = new SqlConnection(builder.ConnectionString))
                using (var cmd = new SqlCommand())
                {
                    conn.Open();
                    cmd.Connection = conn;
    
                    foreach (var sql in SeperateStatements(schemaSql))
                    {
                        cmd.CommandText = sql;
                        cmd.ExecuteNonQuery();
                    }
                }
            }
    
            base.Before(methodUnderTest);
        }
    
        private Stream ReadSchema()
        {
            return typeof(SqlReservationsProgramVisitor<>)
                .Assembly
                .GetManifestResourceStream(
                    "Ploeh.Samples.BookingApi.Sql.BookingDbSchema.sql");
        }
    
        private static IEnumerable<string> SeperateStatements(string schemaSql)
        {
            return schemaSql.Split(
                new[] { "GO" },
                StringSplitOptions.RemoveEmptyEntries);
        }
    
        public override void After(MethodInfo methodUnderTest)
        {
            base.After(methodUnderTest);
    
            var dropCmd = @"
                IF EXISTS (SELECT name
                    FROM master.dbo.sysdatabases
                    WHERE name = N'Booking')
                DROP DATABASE[Booking];";
    
            var builder = new SqlConnectionStringBuilder(
                ConnectionStrings.Reservations);
            builder.InitialCatalog = "Master";
            using (var conn = new SqlConnection(builder.ConnectionString))
            using (var cmd = new SqlCommand(dropCmd, conn))
            {
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
    
    公共类UseDatabaseAttribute:BeforeAfterTestAttribute
    {
    公共覆盖之前无效(MethodInfo methodUnderTest)
    {
    使用(var schemaStream=ReadSchema())
    使用(var rdr=newstreamreader(schemaStream))
    {
    var schemaSql=rdr.ReadToEnd();
    var builder=new-SqlConnectionStringBuilder(
    连接字符串(保留);
    builder.InitialCatalog=“Master”;
    使用(var conn=new SqlConnection(builder.ConnectionString))
    使用(var cmd=new SqlCommand())
    {
    conn.Open();
    cmd.Connection=conn;
    foreach(单独语句中的var-sql(schemaSql))
    {
    cmd.CommandText=sql;
    cmd.ExecuteNonQuery();
    }
    }
    }
    基础。之前(测试中的方法);
    }
    私有流ReadSchema()
    {
    返回类型(SqlReservationsProgramVisitor)
    装配
    .getResourceStream(
    “Ploeh.Samples.BookingApi.Sql.BookingDbSchema.Sql”);
    }
    私有静态IEnumerable SeparateStatements(字符串模式SQL)
    {
    返回schemaSql.Split(
    新[]{“GO”},
    StringSplitOptions.RemoveEmptyEntries);
    }
    公共覆盖之后无效(MethodInfo methodUnderTest)
    {
    基础。之后(方法待测);
    var dropCmd=@”
    如果存在(请选择名称)
    从master.dbo.sysdatabases
    其中name=N‘预订’)
    删除数据库[预订];“;
    var builder=new-SqlConnectionStringBuilder(
    连接字符串(保留);
    builder.InitialCatalog=“Master”;
    使用(var conn=new SqlConnection(builder.ConnectionString))
    使用(var cmd=new SqlCommand(dropCmd,conn))
    {
    conn.Open();
    cmd.ExecuteNonQuery();
    }
    }
    }
    
    您可以看到它正在使用。

    如果我正确理解了ASP.NET Core 3.1的用法,我们现在需要使用
    WebApplicationFactory

    您可以覆盖其
    ConfigureWebHost
    方法来更改配置、依赖项等。我最近用一个假的数据库实现替换了一个真实的数据库实现:

    public class RestaurantApiFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            if (builder is null)
                throw new ArgumentNullException(nameof(builder));
    
            builder.ConfigureServices(services =>
            {
                var descriptors = services
                    .Where(d =>
                        d.ServiceType == typeof(IReservationsRepository))
                    .ToList();
                foreach (var d in descriptors)
                    services.Remove(d);
    
                services.AddSingleton<IReservationsRepository>(
                    new FakeDatabase());
            });
        }
    }
    
    看起来您正在使用xUnit.net。如果是这样,您可以使用其
    BeforeAfterestAttribute
    为每个测试创建和删除数据库

    我通常是这样做的:

    public class UseDatabaseAttribute : BeforeAfterTestAttribute
    {
        public override void Before(MethodInfo methodUnderTest)
        {
            using (var schemaStream = ReadSchema())
            using (var rdr = new StreamReader(schemaStream))
            {
                var schemaSql = rdr.ReadToEnd();
    
                var builder = new SqlConnectionStringBuilder(
                    ConnectionStrings.Reservations);
                builder.InitialCatalog = "Master";
                using (var conn = new SqlConnection(builder.ConnectionString))
                using (var cmd = new SqlCommand())
                {
                    conn.Open();
                    cmd.Connection = conn;
    
                    foreach (var sql in SeperateStatements(schemaSql))
                    {
                        cmd.CommandText = sql;
                        cmd.ExecuteNonQuery();
                    }
                }
            }
    
            base.Before(methodUnderTest);
        }
    
        private Stream ReadSchema()
        {
            return typeof(SqlReservationsProgramVisitor<>)
                .Assembly
                .GetManifestResourceStream(
                    "Ploeh.Samples.BookingApi.Sql.BookingDbSchema.sql");
        }
    
        private static IEnumerable<string> SeperateStatements(string schemaSql)
        {
            return schemaSql.Split(
                new[] { "GO" },
                StringSplitOptions.RemoveEmptyEntries);
        }
    
        public override void After(MethodInfo methodUnderTest)
        {
            base.After(methodUnderTest);
    
            var dropCmd = @"
                IF EXISTS (SELECT name
                    FROM master.dbo.sysdatabases
                    WHERE name = N'Booking')
                DROP DATABASE[Booking];";
    
            var builder = new SqlConnectionStringBuilder(
                ConnectionStrings.Reservations);
            builder.InitialCatalog = "Master";
            using (var conn = new SqlConnection(builder.ConnectionString))
            using (var cmd = new SqlCommand(dropCmd, conn))
            {
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
    
    公共类UseDatabaseAttribute:BeforeAfterTestAttribute
    {
    公共覆盖之前无效(MethodInfo methodUnderTest)
    {
    使用(var schemaStream=ReadSchema())