xUnit理论。如何在测试之间清除数据库。

huangapple go评论83阅读模式
英文:

xUnit Theory. How clear database between tests

问题

我使用 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 = &quot;DataSource=file::memory:?cache=shared&quot;;
        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&lt;TestDatabaseFixture&gt;
    {
        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

就像 @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 = &quot;file:{0}?mode=memory&quot;;  

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)
        =&gt; new DbTrancheTest(
            new DbContextOptionsBuilder&lt;DbTranche&gt;()
            .EnableSensitiveDataLogging(sensitiveDataLoggingEnabled)
            .UseSqlite(string.Format(ConnectionString, DateTime.Now.Ticks))
            .Options);

答案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&lt;DbTranche&gt;()
        .EnableSensitiveDataLogging(false)
        .UseSqlite(string.Format(&quot;DataSource=file::memory:?{0}&quot;, Guid.NewGuid().ToString()))
        .Options);

    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();

huangapple
  • 本文由 发表于 2023年8月10日 20:09:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76875615.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定