英文:
xUnit Theory. How clear database between tests
问题 {#heading}
我使用 xUnit [Theory] 编写测试。我的问题是,我运行第一个测试它通过了,而第二个测试抛出异常 ---- Microsoft.Data.Sqlite.SqliteException:SQLite 错误 19:'UNIQUE constraint failed: CONTRAHENT.ID'。当我分别运行每个测试时,在测试资源管理器中它们都通过了。因此,我认为在第二次运行之前数据库没有被清除/释放。
public class TestDatabaseFixture
{
private const string ConnectionString = "DataSource=file::memory:?cache=shared";
private static readonly object _lock = new();
private static bool _databaseInitialized;
public TestDatabaseFixture()
{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
_databaseInitialized = true;
}
}
}
public DbTrancheTest CreateContext(bool sensitiveDataLoggingEnabled = false)
{
return new DbTrancheTest(
new DbContextOptionsBuilder<DbTranche>()
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.UseSqlite(ConnectionString)
.Options);
}
}
测试类
public class TrancheServiceTest : IClassFixture<TestDatabaseFixture>
{
private TestDatabaseFixture Fixture { get; }
public TrancheServiceTest(TestDatabaseFixture fixture)
{
Fixture = fixture;
}
[Theory]
[InlineData(TrancheStatus.created, true)] // passed
[InlineData(TrancheStatus.accepted, false)] // failed
[InlineData(TrancheStatus.chanaged, true)] // failed
[InlineData(TrancheStatus.waitforconf, false)] // failed
[InlineData(TrancheStatus.rejected, false)] // failed
public void Should_Set_Can_Edit_Flag_For_User(TrancheStatus status, bool result)
{
// Arrange
using var context = Fixture.CreateContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var orderRepository = new TrancheOrderRepository(context);
var userRepo = new UserRepository(context);
var contrahentRepository = new ContrahentRepository(context);
contrahentRepository.Add(new CONTRAHENT { ID = 1, NAME = "Contrahent1" });
contrahentRepository.Save();
var trancheOrder = new TRANCHE_ORDER
{
ID = 1,
ISSUE_DATE = DateTime.Parse("2023-02-01"),
LAST_UPDATE = DateTime.Parse("2023-02-01"),
STATUS = (short)status,
VOLUME = 0,
USER = "",
};
orderRepository.Add(trancheOrder);
orderRepository.Save();
userRepo.Add(new USER { ID = 1, CAN_MODIFY = 1, DESCRIPTION = "Test user", FK_CONTRAHENT_ID = 1, NAME = "User1", USER_ID = 123 });
userRepo.Save();
var service = new TrancheService(contrahentRepository, orderRepository, userRepo);
DateTime dateFrom = DateTime.Parse("2023-01-01");
DateTime dateTo = DateTime.Parse("2023-12-01");
// Act
var trancheList = service.GetTrancheList(dateFrom, dateTo, 123, true);
// Assert
Assert.Collection(trancheList,
e => Assert.Equal(result, e.CanEdit));
}
}
希望这对你有所帮助。 英文:
I write test using xUnit [Theory]. My problem is that I run first test and it pass, while 2nd test throw exception ---- Microsoft.Data.Sqlite.SqliteException : SQLite Error 19: 'UNIQUE constraint failed: CONTRAHENT.ID'. When I run each test separately, in Test Explorer they all passed. So I assume that database is not cleared/disposed before second run.
public class TestDatabaseFixture
{
private const string ConnectionString = "DataSource=file::memory:?cache=shared";
private static readonly object _lock = new();
private static bool _databaseInitialized;
public TestDatabaseFixture()
{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
_databaseInitialized = true;
}
}
}
public DbTrancheTest CreateContext(bool sensitiveDataLoggingEnabled = false)
=&gt; new DbTrancheTest(
new DbContextOptionsBuilder&lt;DbTranche&gt;()
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.UseSqlite(ConnectionString)
.Options);
}
Test class
public class TrancheServiceTest : IClassFixture<TestDatabaseFixture>
{
private TestDatabaseFixture Fixture { get; }
// private readonly ITestOutputHelper output;
public TrancheServiceTest(TestDatabaseFixture fixture)
{
Fixture = fixture;
}
[Theory]
[InlineData(TrancheStatus.created, true)] // passed
[InlineData(TrancheStatus.accepted, false)] // failed
[InlineData(TrancheStatus.chanaged, true)] // failed
[InlineData(TrancheStatus.waitforconf, false)] // failed
[InlineData(TrancheStatus.rejected, false)] //failed
public void Should_Set_Can_Edit_Flag_For_User(TrancheStatus status, bool result)
{
// Arrange
using var context = Fixture.CreateContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var orderRepository = new TrancheOrderRepository(context);
var userRepo = new UserRepository(context);
var contrahentRepository = new ContrahentRepository(context);
contrahentRepository.Add(new CONTRAHENT { ID = 1, NAME = &quot;Contrahent1&quot; });
contrahentRepository.Save();
var trancheOrder = new TRANCHE_ORDER
{
ID = 1,
ISSUE_DATE = DateTime.Parse(&quot;2023-02-01&quot;),
LAST_UPDATE = DateTime.Parse(&quot;2023-02-01&quot;),
STATUS = (short)status,
VOLUME = 0,
USER = &quot;&quot;,
};
orderRepository.Add(trancheOrder);
orderRepository.Save();
userRepo.Add(new USER { ID = 1, CAN_MODIFY = 1, DESCRIPTION = &quot;Test user&quot;, FK_CONTRAHENT_ID = 1, NAME = &quot;User1&quot;, USER_ID = 123 });
userRepo.Save();
var service = new TrancheService(contrahentRepository, orderRepository, userRepo);
DateTime dateFrom = DateTime.Parse(&quot;2023-01-01&quot;);
DateTime dateTo = DateTime.Parse(&quot;2023-12-01&quot;);
// Act
var trancheList = service.GetTrancheList(dateFrom, dateTo, 123, true);
// Assert
Assert.Collection(trancheList,
e =&gt; Assert.Equal(result, e.CanEdit));
}
`}
`
答案1 {#1}
得分: 1
就像 @fildor 所说的那样,您可以每次创建一个新的数据库。为此,您可以使用一个类似这样的 连接字符串:
ConnectionString = "file:{0}?mode=memory";
然后按照以下方式更新您的代码:
public TestDatabaseFixture()
{
lock (_lock)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
}
}
public DbTrancheTest CreateContext(bool sensitiveDataLoggingEnabled = false)
=> new DbTrancheTest(
new DbContextOptionsBuilder<DbTranche>()
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.UseSqlite(string.Format(ConnectionString, DateTime.Now.Ticks))
.Options);
英文:
Just like @fildor said, you can create a new DB each time. For that, you can use a connection string which looks like this:
ConnectionString = "file:{0}?mode=memory";
And update your code like this:
public TestDatabaseFixture()
{
lock (_lock)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
}
}
}
`public DbTrancheTest CreateContext(bool sensitiveDataLoggingEnabled = false)
=> new DbTrancheTest(
new DbContextOptionsBuilder<DbTranche>()
.EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
.UseSqlite(string.Format(ConnectionString, DateTime.Now.Ticks))
.Options);
`
答案2 {#2}
得分: 0
我最终在每个单独的测试中创建了上下文,就像这样。所有的测试都通过了。
使用 var context = new DbTrancheTest(
new DbContextOptionsBuilder<DbTranche>()
.EnableSensitiveDataLogging(false)
.UseSqlite(string.Format("DataSource=file::memory:?{0}", Guid.NewGuid().ToString()))
.Options);
`context.Database.EnsureDeleted();
context.Database.EnsureCreated();
`
英文:
I end up creating context in each individual test like this. All tests passed.
using var context = new DbTrancheTest(
new DbContextOptionsBuilder<DbTranche>()
.EnableSensitiveDataLogging(false)
.UseSqlite(string.Format("DataSource=file::memory:?{0}", Guid.NewGuid().ToString()))
.Options);
context.Database.EnsureDeleted();
context.Database.EnsureCreated();