Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何模拟实体框架';s FromSqlRaw方法?_C#_Asp.net Core_Entity Framework Core_Xunit_Nsubstitute - Fatal编程技术网

C# 如何模拟实体框架';s FromSqlRaw方法?

C# 如何模拟实体框架';s FromSqlRaw方法?,c#,asp.net-core,entity-framework-core,xunit,nsubstitute,C#,Asp.net Core,Entity Framework Core,Xunit,Nsubstitute,我正在编写一个单元测试,需要模拟实体框架的.FromSqlRaw方法。在被测类中执行该方法时,会引发以下异常: System.InvalidOperationException:没有方法 类型上的“FromSqlOnQueryable” “Microsoft.EntityFrameworkCore.RelationalQueryableExtensions”表示 匹配指定的参数 以下是正在测试的类: public class PowerConsumptionRepository : IPower

我正在编写一个单元测试,需要模拟实体框架的.FromSqlRaw方法。在被测类中执行该方法时,会引发以下异常:

System.InvalidOperationException:没有方法 类型上的“FromSqlOnQueryable” “Microsoft.EntityFrameworkCore.RelationalQueryableExtensions”表示 匹配指定的参数

以下是正在测试的类:

public class PowerConsumptionRepository : IPowerConsumptionRepository
    {
        private readonly IDatabaseContext _databaseContext;
        private readonly IDateTimeHelper _dateTimeHelper;

        public PowerConsumptionRepository(IDatabaseContext databaseContext, IDateTimeHelper dateTimeHelper)
        {
            _databaseContext = databaseContext;
            _dateTimeHelper = dateTimeHelper;
        }
        public List<IntervalCategoryConsumptionModel> GetCurrentPowerConsumption(string siteId)
        {
            var currentDate = _dateTimeHelper
                .ConvertUtcToLocalDateTime(DateTime.UtcNow, ApplicationConstants.LocalTimeZone)
                .ToString("yyyy-MM-dd");
            var currentDateParameter = new SqlParameter("currentDate", currentDate);
            var measurements = _databaseContext.IntervalPowerConsumptions
                .FromSqlRaw(SqlQuery.CurrentIntervalPowerConsumption, currentDateParameter)
                .AsNoTracking()
                .ToList();
            return measurements;
        }
    }
公共类PowerConsumptionRepository:IPowerConsumptionRepository
{
私有只读IDatabaseContextu databaseContext;
私有只读IDateTimeHelper\u dateTimeHelper;
公共功耗存储库(IDatabaseContext databaseContext,IDateTimeHelper dateTimeHelper)
{
_databaseContext=databaseContext;
_dateTimeHelper=dateTimeHelper;
}
公共列表GetCurrentPowerConsumption(字符串siteId)
{
var currentDate=\u dateTimeHelper
.ConvertutCTOCLocalDateTime(DateTime.UtcNow,ApplicationConstants.LocalTimeZone)
.ToString(“yyyy-MM-dd”);
var currentDateParameter=新的SqlParameter(“currentDate”,currentDate);
var度量=_databaseContext.IntervalPowerConsumptions
.FromSqlRaw(SqlQuery.CurrentIntervalPowerConsument,currentDateParameter)
.AsNoTracking()
.ToList();
回波测量;
}
}
单元测试:


    public class PowerConsumptionRepositoryTests
    {
        [Fact]
        public void TestTest()
        {
            var data = new List<IntervalCategoryConsumptionModel>
            {
                new IntervalCategoryConsumptionModel
                {
                    Id = 1,
                    Hvac = 10                    
                },
                new IntervalCategoryConsumptionModel
                {
                    Id = 1,
                    Hvac = 10
                }
            }.AsQueryable();
            var dateTimeHelper = Substitute.For<IDateTimeHelper>();
            dateTimeHelper.ConvertUtcToLocalDateTime(Arg.Any<DateTime>(), Arg.Any<string>()).Returns(DateTime.Now);
            var mockSet = Substitute.For<DbSet<IntervalCategoryConsumptionModel>, IQueryable<IntervalCategoryConsumptionModel>>();
            ((IQueryable<IntervalCategoryConsumptionModel>)mockSet).Provider.Returns(data.Provider);
            ((IQueryable<IntervalCategoryConsumptionModel>)mockSet).Expression.Returns(data.Expression);
            ((IQueryable<IntervalCategoryConsumptionModel>)mockSet).ElementType.Returns(data.ElementType);
            ((IQueryable<IntervalCategoryConsumptionModel>)mockSet).GetEnumerator().Returns(data.GetEnumerator());
            var context = Substitute.For<IDatabaseContext>();
            context.IntervalPowerConsumptions = (mockSet);
            var repo = new PowerConsumptionRepository(context, dateTimeHelper);
            var result = repo.GetCurrentPowerConsumption(Arg.Any<string>());
            result.Should().NotBeNull();
        }
    }

公共类功耗存储测试
{
[事实]
公共void TestTest()
{
var数据=新列表
{
新的IntervalCategoryConsumption模型
{
Id=1,
Hvac=10
},
新的IntervalCategoryConsumption模型
{
Id=1,
Hvac=10
}
}.AsQueryable();
var dateTimeHelper=Substitute.For();
dateTimeHelper.ConvertutCTLocalDateTime(Arg.Any(),Arg.Any()).Returns(DateTime.Now);
var mockSet=Substitute.For();
((IQueryable)mockSet.Provider.Returns(data.Provider);
((IQueryable)mockSet.Expression.Returns(data.Expression);
((IQueryable)mockSet.ElementType.Returns(data.ElementType);
((IQueryable)mockSet.GetEnumerator()。返回(data.GetEnumerator());
var context=Substitute.For();
context.intervalpowerconsumpions=(mockSet);
var repo=新的PowerConsumptionRepository(上下文,dateTimeHelper);
var result=repo.getCurrentPowerConsument(Arg.Any());
result.Should().NotBeNull();
}
}

内存中的提供程序无法执行此操作,因为它是一个关系操作。忽略哲学的一面,也许有几种方法可以解决它

  • 模拟查询提供程序
  • 在封面下,它通过
    IQueryProvider.CreateQuery(表达式)
    方法运行,因此您可以使用模拟框架拦截调用并返回所需内容。这就是为什么(免责声明我是作者)。这就是我如何在代码中从SQL*调用单元测试

  • 更好的内存提供程序
  • 我没有太多使用它,但我的理解是像SQLite这样的提供商可能会支持它

    为了解决OP评论,请考虑您是否应该使用内存提供程序/模拟
    DbContext
    ,我们在个人意见范围内。我的观点是,我对使用内存提供程序没有任何保留,它易于使用,速度相当快,并且对许多人都很好。我同意你不应该嘲笑
    DbContext
    ,仅仅因为这真的很难做到。它本身并不模拟
    DbContext
    ,而是封装了内存中的提供程序,并使用流行的模拟框架来提供对
    FromSql*
    ExecuteSql*
    之类的支持


    我读了Jimmy Bogard(我非常尊敬他)的链接文章,但是在这个话题上,我并不完全同意。在很少的情况下,我的数据访问层中有原始SQL,通常是调用一个存储过程或函数,该存储过程或函数已经过测试/在SUT之外有测试。我通常把他们当作一种依赖;我应该能够为我的SUT编写单元测试,使用该依赖项返回充分测试我的SUT所需的值。

    使用
    。从SQLRAW
    您将原始sql查询发送到数据库引擎。
    如果您确实想测试您的应用程序(
    .FromsqlRaw
    )是否按预期工作,请针对实际数据库进行测试

    是的,它是较慢的,是的,它需要运行带有一些测试数据的数据库-是的,它将为您的应用程序工作提供强大的信心


    所有其他测试(模拟测试、内存测试或sqlite测试)都会让您产生错误的信心。

    在我的场景中,我使用SQLRAW的
    方法调用数据库中的存储过程。
    对于EntityFramework Core(版本3.1肯定运行良好),我是这样做的:

    将虚拟方法添加到
    DbContext
    类:

    public virtual IQueryable<TEntity> RunSql<TEntity>(string sql, params object[] parameters) where TEntity : class
    {
        return this.Set<TEntity>().FromSqlRaw(sql, parameters);
    }
    
    调用新的
    RunSql
    方法,而不是从sqlraw
    调用

    // Before
    //var resut = dbContext.FromSqlRaw<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
    // New
    var result = dbContext.RunSql<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
    

    To:我们从不试图模仿DbContext或IQueryable。这样做是困难、麻烦和脆弱的。不要这样做。(他们的重点)对。我理解。但这是否意味着我无法对其进行单元测试?我不能使用内存中的数据库,因为
    。FromSqlRaw
    执行SQL查询。那不是问题所在吗?您正在测试一个与数据库高度耦合的类,现在您想抽象该数据库,但发现它非常困难或不可能?要正确地测试PowerConsumptionRepository,您可能应该使用“真实”数据库。我最近偶然发现了吉米·博加德的那篇文章,也许
    // Before
    //var resut = dbContext.FromSqlRaw<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
    // New
    var result = dbContext.RunSql<YourTable>("SELECT * FROM public.stored_procedure({0}, {1})", 4, 5).ToListAsync();
    
    public static class QueryableExtensions
    {
        public static IQueryable<T> AsAsyncQueryable<T>(this IEnumerable<T> input)
        {
            return new NotInDbSet<T>( input );
        }
    
    }
    
    public class NotInDbSet< T > : IQueryable<T>, IAsyncEnumerable< T >, IEnumerable< T >, IEnumerable
    {
        private readonly List< T > _innerCollection;
        public NotInDbSet( IEnumerable< T > innerCollection )
        {
            _innerCollection = innerCollection.ToList();
        }
    
        public IAsyncEnumerator< T > GetAsyncEnumerator( CancellationToken cancellationToken = new CancellationToken() )
        {
            return new AsyncEnumerator( GetEnumerator() );
        }
    
        public IEnumerator< T > GetEnumerator()
        {
            return _innerCollection.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public class AsyncEnumerator : IAsyncEnumerator< T >
        {
            private readonly IEnumerator< T > _enumerator;
            public AsyncEnumerator( IEnumerator< T > enumerator )
            {
                _enumerator = enumerator;
            }
    
            public ValueTask DisposeAsync()
            {
                return new ValueTask();
            }
    
            public ValueTask< bool > MoveNextAsync()
            {
                return new ValueTask< bool >( _enumerator.MoveNext() );
            }
    
            public T Current => _enumerator.Current;
        }
    
        public Type ElementType => typeof( T );
        public Expression Expression => Expression.Empty();
        public IQueryProvider Provider => new EnumerableQuery<T>( Expression );
    }