这是在xunit测试.NET Core中获取上下文的正确方法吗?

时间:2019-07-28 07:04:20

标签: c# asp.net-core .net-core integration-testing xunit

在测试中,我必须有权访问数据库上下文(真实数据库,它不在内存中)。因为发布后,我想确保记录已保存在数据库中。

这就是我所拥有的,它可以完美运行,但是我没有足够的经验来确保这是获取上下文的正确方法。

public class ValuesControllerTests : TestHostFixture
{
    [Theory]
    [InlineData("/values/sample")]
    public async Task Sample_WhenCreatingSampleData_ThenIsAddedToDatabase(string url)
    {
        // given
        var command = new AddSampleCommand { Name = "TestRecord" };

        // when
        var httpResponse = await Client.PostAsJsonAsync(url, command);

        // then
        httpResponse.EnsureSuccessStatusCode();

        using (var dbContext = GetContext())
        {
            dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord").ShouldNotBeNull();
        }
    }
}


public abstract class TestHostFixture : IClassFixture<CustomWebApplicationFactory<Startup>>
{
    protected readonly HttpClient Client;
    private readonly CustomWebApplicationFactory<Startup> _factory;

    protected TestHostFixture()
    {
        _factory = new CustomWebApplicationFactory<Startup>();
        Client = _factory.CreateClient();
    }

    protected MyContext GetContext()
    {
        return _factory.Server.Host.Services.CreateScope().ServiceProvider.GetService<MyContext>();
    }
}

因此,总而言之-在测试中,我通过以下方式获取上下文:

using (var dbContext = GetContext())
{
    dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord").ShouldNotBeNull();
}

和GetContext方法:

protected MyContext GetContext()
{
    return _factory.Server.Host.Services.CreateScope().ServiceProvider.GetService<MyContext>();
}

请让我知道这是否可以,或者由于将来可能出现的问题,我应该以某种方式进行重构。

2 个答案:

答案 0 :(得分:1)

通常,这很好:您绝对应该利用主机的DI容器来检索数据库上下文。由于数据库上下文是作用域的,因此创建新的服务范围来检索上下文也是正确的。

但是,由于通常由DI容器管理它创建的对象的生存期,因此应该将处理数据库上下文的工作留给DI容器,而不是服务范围。

尽管在单元测试中可能没有多大关系,但是无论如何它都会很快被清除(并且由于您正在处理上下文,因此您也不会泄漏数据库连接),但从长远来看,它仍然是更好的样式和更安全的方法运行。

因此,请处置服务范围:

// …
httpResponse.EnsureSuccessStatusCode();

using (var scope = Host.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetService<MyContext>();
    var item = dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord");
    item.ShouldNotBeNull();
}

答案 1 :(得分:0)

在对poke提供的实现进行改进的情况下,您可以考虑创建一个委托来处理对所创建范围的正确处理。

例如

protected void GetContext(Action<MyContext> test) {
    using(var scope = _factory.Server.Host.Services.CreateScope()) {
        var context = scope.ServiceProvider.GetRequiredService<MyContext>();
        test(context);
    }
}

在进行测试时,只需致电代表

[Theory]
[InlineData("/values/sample")]
public async Task Sample_WhenCreatingSampleData_ThenIsAddedToDatabase(string url) {
    // given
    var command = new AddSampleCommand { Name = "TestRecord" };

    // when
    var httpResponse = await Client.PostAsJsonAsync(url, command);

    // then
    httpResponse.EnsureSuccessStatusCode();

    GetContext(dbContext => {
        var item = dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord");
        item.ShouldNotBeNull();
    });
}