首页 > 解决方案 > 尝试编写测试时的 NSubstitute、DbContext 和一个奇怪的问题

问题描述

我有一个很好的模拟 DbSet的扩展方法:

public static class DbSetExtensions
{
    public static DbSet<T> ToDbSet<T>(this IEnumerable<T> data) where T : class
    {
        var queryData = data.AsQueryable();
        var dbSet = Substitute.For<DbSet<T>, IQueryable<T>>();
        ((IQueryable<T>)dbSet).Provider.Returns(queryData.Provider);
        ((IQueryable<T>)dbSet).Expression.Returns(queryData.Expression);
        ((IQueryable<T>)dbSet).ElementType.Returns(queryData.ElementType);
        ((IQueryable<T>)dbSet).GetEnumerator().Returns(queryData.GetEnumerator());

        return dbSet;
    }
}

我试图在这样的上下文文件中使用它:

public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public T WhenListSucceeds<TEntity>(IList<TEntity> data) where TEntity : class
    {
        var dbSet = data.ToDbSet();
        DatabaseContext.Set<TEntity>().Returns(dbSet);

        return (T)this;
    }

    public T WhenGetSucceeds<TEntity>(TEntity entity) where TEntity : class
    {
        var dbSet = new List<TEntity> { entity }.ToDbSet();
        DatabaseContext.Set<TEntity>().Returns(dbSet);

        return (T)this;
    }
}

当我对此方法运行测试时,它失败了:

public ActionResult<List<Formula>> ListFormulas(int id) =>
    Ok(_databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList());

带有此错误消息:

System.InvalidCastException:无法将“Castle.Proxies.ObjectProxy_3”类型的对象转换为“Microsoft.EntityFrameworkCore.Metadata.Internal.Model”类型。

所以我试着把它分解一下。首先,我将方法更改为:

public ActionResult<List<Formula>> ListFormulas(int id)
{
    var s = _databaseContext.Formulas;
    var x = _databaseContext.Formulas.ToList();
    var t = _databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList();

    return Ok(t);
}

但是在调试时,代码并没有通过该ToList()方法。我仍然遇到同样的问题。因此,我将代码更改为:

public ActionResult<List<Formula>> ListFormulas(int id)
{
    var p = _databaseContext.Set<Formula>();
    var q = p.ToList();


    var s = _databaseContext.Formulas;
    var x = _databaseContext.Formulas.ToList();
    var t = _databaseContext.Formulas.Where(m => m.AttributeId.Equals(id)).ToList();

    return Ok(t);
}

前 3 行代码有效,但一旦到达该行var x = _databaseContext.Formulas.ToList();,它就会失败。有谁知道为什么?

这是测试:

[TestFixture]
public class ListShould
{
    [Test]
    public void ReturnList()
    {
        // Assemble
        var services = GenericOrderProviderContext.GivenServices();
        var provider = services.WhenCreateOrderProvider();

        services.DatabaseContext.Attributes = new List<Attribute>().ToDbSet();
        services.DatabaseContext.Set<Attribute>().ReturnsForAnyArgs(_ => new List<Attribute>().ToDbSet());

        // Act
        var result = provider.List();

        // Assert
        result.Failure.Should().BeFalse();
        result.Result.Count().Should().Be(0);
    }
}

标签: c#unit-testingdbcontextnsubstitute

解决方案


.Formulas当未配置db 上下文属性时,我能够重现您的错误。如果您同时使用两者.Set<Formula>(),则.Formulas需要同时配置两者。

我确实注意到您为 db set 枚举器设置的

((IQueryable<T>)dbSet).GetEnumerator().Returns(queryData.GetEnumerator());

导致我之前看到的一些行为,其中只有第一个 ToList() 调用返回结果。如果你明白了,你可能需要重置枚举器或使用Func<CallInfo, IEnumerator<Formula>> Returns重载。


推荐阅读