entity-framework-core - 让 EF Core 将 SQL 语句输出到 xUnit 的 ITestOutputHelper

标签 entity-framework-core xunit xunit.net ef-core-2.2

我正在使用 EF Core 2.2.4 并试图找出 EF Core 在我们的单元测试中发送到我们的 SQLite 数据库的 SQL 语句。由于我们使用的是 xUnit (2.4.1),我们必须将日志消息写入 ITestOutputHelper xUnit 实例正在注入(inject)我们的测试类而不是控制台。对于控制台,我找到了这段代码:

private static ILoggerFactory GetLoggerFactory()
{
    IServiceCollection serviceCollection = new ServiceCollection();
    serviceCollection.AddLogging(builder =>
        builder.AddConsole()
            .AddFilter(DbLoggerCategory.Database.Command.Name,
                LogLevel.Information));
    return serviceCollection.BuildServiceProvider()
        .GetService<ILoggerFactory>();
}

如何将此输出重定向到 ITestOutputHelper.WriteLine()

最佳答案

首先,创建一些样板日志记录代码以允许输出到 ITestOutputHelper:

class TestLoggerProvider : ILoggerProvider
{
    ITestOutputHelper _output;

    public TestLoggerProvider(ITestOutputHelper output)
        => _output = output;

    public ILogger CreateLogger(string categoryName)
        => new TestLogger(categoryName, _output);

    public void Dispose()
    {
    }
}

class TestLogger : ILogger
{
    string _categoryName;
    ITestOutputHelper _output;

    public TestLogger(string categoryName, ITestOutputHelper output)
    {
        _categoryName = categoryName;
        _output = output;
    }

    public bool IsEnabled(LogLevel logLevel)
        // NB: Only logging things related to commands, but you can easily expand
        //     this
        => _categoryName == DbLoggerCategory.Database.Command.Name;

    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception exception,
        Func<TState, Exception, string> formatter)
    {
        // TODO: Customize the formatting even more if you want
        //if (eventId == RelationalEventId.CommandExecuting)
        //{
        //    var structure = (IReadOnlyList<KeyValuePair<string, object>>)state;
        //    var parameters = (string)structure.First(i => i.Key == "parameters")
        //        .Value;
        //    var commandText = (string)structure.First(i => i.Key == "commandText")
        //        .Value;
        //}

        _output.WriteLine(formatter(state, exception));
    }

    public IDisposable BeginScope<TState>(TState state)
        => null;
}

接下来,确保您的 DbContext 可以接受外部选项。

class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }
}

最后,把它连接起来。下面是为每个测试创建一个新上下文的示例。使用 classcollection fixture以延长上下文的生命周期。

public class UnitTest1 : IDisposable
{
    IServiceProvider _serviceProvider;
    MyDbContext _db;

    public UnitTest1(ITestOutputHelper output)
    {
        _serviceProvider = new ServiceCollection()
            .AddLogging(x => x.AddProvider(new TestLoggerProvider(output)))
            .AddEntityFrameworkSqlite()
            .BuildServiceProvider();

        _db = new MyDbContext(
            new DbContextOptionsBuilder<MyDbContext>()
                // Don't call UseLoggerFactory! (a new service provider would be
                // created every time without ever getting disposed)
                .UseInternalServiceProvider(_serviceProvider)
                .UseSqlite("Data Source=:memory:")
                .Options);
    }

    [Fact]
    public void Test1()
    {
        _db.Database.ExecuteSqlRaw("-- Can you see me?");
    }

    public void Dispose()
    {
        _db.Dispose();
        (_serviceProvider as IDisposable)?.Dispose();
    }
}

关于entity-framework-core - 让 EF Core 将 SQL 语句输出到 xUnit 的 ITestOutputHelper,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61673470/

相关文章:

bash - Linux Bash 上的 "dotnet ef dbcontext scaffold"问题

c# - EF Core Find 方法等效于多个记录?

c# - Moq 和 xUnit 的单元测试失败 - InvalidOperationException

c# - 自动夹具和 AutoMoq : Overriding object generation behavior

c# - xUnit 非静态成员数据

c# - 如何分离 NCrunch 节点服务使用的所有 LocalDb 数据库

c# - 如何从 Visual Studio Team Services 运行 ASP.NET Core Entity Framework 迁移

c# - 使用 EF Core 编写动态 LINQ 查询以进行排序和投影

F# 单元测试和模式匹配断言

unit-testing - XUnit 是否跨测试类共享 fixture 实例?