xUnit InMemoryDatabase测试失败:InvalidOperationException

时间:2018-11-12 15:02:48

标签: c# xunit in-memory-database ef-core-2.1 .net-core-2.1

我在CI环境中的TFS上运行单元测试,但今天失败了,但有以下异常:

Error:
  System.InvalidOperationException : Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
Stacktrace:
    at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
    at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
    at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.GetDisplayName(Type type)
    at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FindEntityType(Type type)
    at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.EntityEqualityRewritingExpressionVisitor.RewriteEntityEquality(ExpressionType nodeType, Expression left, Expression right)
    at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.EntityEqualityRewritingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
    at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
    at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
    at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
    at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.EntityEqualityRewritingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
    at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
    at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
    at Remotion.Linq.Clauses.WhereClause.TransformExpressions(Func`2 transformation)
    at Remotion.Linq.QueryModel.TransformExpressions(Func`2 transformation)
    at Microsoft.EntityFrameworkCore.Query.Internal.QueryOptimizer.Optimize(QueryCompilationContext queryCompilationContext, QueryModel queryModel)
    at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.OptimizeQueryModel(QueryModel queryModel, Boolean asyncQuery)
    at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor[TResult](QueryModel queryModel)
    at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database, IDiagnosticsLogger`1 logger, Type contextType)
    at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass13_0`1.<Execute>b__0()
    at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
    at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
    at System.Linq.Queryable.Any[TSource](IQueryable`1 source)
    at ModelNamespace.SomeRepository.TestMethod()
    at TestNamespace.SomeRepositoryTest.TestMethod_Test()

此后,我再次在相同的提交上启动了相同的构建,并且成功。是不是很奇怪?

如何建立测试

要设置InMemoryDatabase,我们使用以下帮助程序类:

public class DbContextTestHelper
{
    public static DbContextOptions<CustomContext> PrepareData(Action<CustomContext> createData)
    {
        var options = CreateDbContextOptions();
        SaveData(options, createData);
        return options;
    }

    private static void SaveData(DbContextOptions<CustomContext> options, Action<CustomContext> createData)
    {
        // Insert seed data into the database using one instance of the context
        using (var context = new CustomContext(options))
        {
            createData.Invoke(context);
            context.SaveChanges();
        }
    }

    private static DbContextOptions<CustomContext> CreateDbContextOptions()
    {
        return new DbContextOptionsBuilder<CustomContext>()
            .UseInMemoryDatabase(Guid.NewGuid()
                .ToString()) //Database with same name gets reused, so let's isolate the tests from each other...
            .Options;
    }
}

所以现在我们建立了许多这样的测试:

public class SomeRepositoryTest
{
    [Fact]
    public async Task TestMethod_Test()
    {
        var options = DbContextTestHelper.PrepareData(context =>
        {
            // do some initialisation
        });
        // Use a clean instance of the context to run the test
        using (var context = new CustomContext(options))
        {
            var testee = new SomeRepository(context);
            await testee.TestMethod(); // InvalidOperationException from above
            // Assert something
        }
    }
}

当然,在许多情况下,我们会创建DbContextOptions<CustomContext>,并且由于xUnit提供的内置功能,它们自然会并行运行(当然,不是在同一个类中,而是用于许多测试)类)。

我的问题

据我所知,为何有时会导致异常的可能性很小:

    生成
  • GUID以确保InMemoryDatabase的不同实例是重复的。但是我宁可不相信这一点。
  • 内存数据库系统中有一个错误
  • 我们使用InMemoryDatabases错误

你知道发生了什么吗?

编辑1:

根据要求,这里是TestMethod()被调用的System.Linq.Queryable.Any[TSource](IQueryable'1 source)

    public void TestMethod(SomeObject someObject, List<DateTime> dates, bool someOption)
    {
        var res = _dbContext.SomeSet.Where(o =>
            o.SomeObject.Equals(someObject) && dates.Contains(o.DateTime) && o.SomeOption == someOption);
        if (res.Any()) // at System.Linq.Queryable.Any[TSource](IQueryable`1 source) 
        {
            _dbContext.SomeSet.RemoveRange(res);
        }
    }

0 个答案:

没有答案