gpt4 book ai didi

sqlite - 如何在 Entity Framework Core 中对事务进行单元测试?

转载 作者:行者123 更新时间:2023-12-03 17:42:33 25 4
gpt4 key购买 nike

我有一个在事务中做一些工作的方法:

public async Task<int> AddAsync(Item item)
{
int result;

using (var transaction = await _context.Database.BeginTransactionAsync())
{
_context.Add(item);

// Save the item so it has an ItemId
result = await _context.SaveChangesAsync();

// perform some actions using that new item's ItemId
_otherRepository.Execute(item.ItemId);

transaction.Commit();
}

return result;
}

我想添加单元测试来检查如果 _context.SaveChangesAsync_otherRepository.Execute 失败则事务回滚,这可能吗?

我看不到使用 InMemory 的方法或 SQLite ?

最佳答案

@Ilya Chumakov 的出色回答让我能够对交易进行单元测试。我们在评论中的讨论随后暴露了一些有趣的观点,我认为这些观点值得进入答案,这样它们会更持久、更容易看到:

主要的一点是,Entity Framework 记录的事件依赖于数据库提供程序,这让我很吃惊。如果使用 InMemory 提供程序,您只会得到一个事件:

  1. 编号:1;执行命令

如果你使用 Sqlite 作为内存数据库,你会得到四个事件:

  1. 编号:1;执行命令
  2. 身份证号:5;开始交易
  3. 编号:1;执行命令
  4. 编号:6;提交交易

我没想到记录的事件会根据数据库提供程序而改变。

对于任何想要了解更多的人,我通过更改 Ilya 的日志记录代码来捕获事件详细信息,如下所示:

    public class FakeLogger : ILogger
{
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
var record = new LogRecord
{
EventId = eventId.Id,
RelationalEventId = (RelationalEventId) eventId.Id,
Description = formatter(state, exception)
};

Events.Add(record);

}

public List<LogRecord> Events { get; set; } = new List<LogRecord>();

public bool IsEnabled(LogLevel logLevel) => true;

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

public class LogRecord
{
public EventId EventId { get; set; }
public RelationalEventId RelationalEventId { get; set; }
public string Description { get; set; }
}

然后我调整了返回内存数据库的代码,以便我可以如下切换内存数据库提供程序:

    public class InMemoryDatabase
{
public FakeLogger EfLogger { get; private set; }

public MyDbContext GetContextWithData(bool useSqlite = false)
{
EfLogger = new FakeLogger();

var factoryMock = Substitute.For<ILoggerFactory>();
factoryMock.CreateLogger(Arg.Any<string>()).Returns(EfLogger);

DbContextOptions<MyDbContext> options;

if (useSqlite)
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();

options = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlite(connection)
.UseLoggerFactory(factoryMock)
.Options;
}
else
{
options = new DbContextOptionsBuilder<MyDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
// don't raise the error warning us that the in memory db doesn't support transactions
.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.UseLoggerFactory(factoryMock)
.Options;
}

var ctx = new MyDbContext(options);

if (useSqlite)
{
ctx.Database.EnsureCreated();
}

// code to populate the context with test data

ctx.SaveChanges();

return ctx;
}
}

最后,在我的单元测试中,我确保在我的测试的断言部分之前清除事件日志,以确保我不会因为在我的测试安排部分记录的事件而得到误报:

        public async Task Commits_transaction()
{
using (var context = _inMemoryDatabase.GetContextWithData(useSqlite: true))
{

// Arrange
// code to set up date for test

// make sure none of our setup added the event we are testing for
_inMemoryDatabase.EfLogger.Events.Clear();

// Act
// Call the method that has the transaction;

// Assert
var result = _inMemoryDatabase.EfLogger.Events
.Any(x => x.EventId.Id == (int) RelationalEventId.CommittingTransaction);

关于sqlite - 如何在 Entity Framework Core 中对事务进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44088436/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com