Asp.net core 如何对.NET Core 3.1中使用ExecuteSqlRawAsync调用存储过程的函数进行单元测试?

Asp.net core 如何对.NET Core 3.1中使用ExecuteSqlRawAsync调用存储过程的函数进行单元测试?,asp.net-core,stored-procedures,entity-framework-core,xunit.net,asp.net-core-3.1,Asp.net Core,Stored Procedures,Entity Framework Core,Xunit.net,Asp.net Core 3.1,我正在ASP.NET Core 3.1中编写一个API,使用EF Core访问SQL Server数据库。我在API中有一个函数,它需要调用一个包含多个输入参数和一个输出参数的存储过程。下面是此函数的简化版本 我将DbContext与一起使用。对于其他测试,请使用内存数据库(),但内存中的数据库不能与存储过程一起使用 (此解决方案是数据库优先,而不是代码优先。如果必要,可以更改存储过程,但如果不必更改则更好。如果有帮助,我可以更改C#函数以不同的方式调用存储过程。) 如何对该函数进行单元测试 公

我正在ASP.NET Core 3.1中编写一个API,使用EF Core访问SQL Server数据库。我在API中有一个函数,它需要调用一个包含多个输入参数和一个输出参数的存储过程。下面是此函数的简化版本

我将
DbContext
一起使用。对于其他测试,请使用内存数据库()
,但内存中的数据库不能与存储过程一起使用

(此解决方案是数据库优先,而不是代码优先。如果必要,可以更改存储过程,但如果不必更改则更好。如果有帮助,我可以更改C#函数以不同的方式调用存储过程。)

如何对该函数进行单元测试

公共类MyFoo:IFoo
{
公共应用程序DbContext DbContext{get;}
公共MyFoo(ApplicationDbContext dbContext)
{
DbContext=DbContext;
}
公共异步任务GetMyStoredProcResult(字符串val1、字符串val2、字符串val3、字符串val4、字符串val5)
{
//为简洁起见,已删除输入验证
var p1=新的SqlParameter
{
ParameterName=“p1”,
DbType=System.Data.DbType.String,
方向=System.Data.ParameterDirection.Input,
值=值1
};
//为了简洁起见,删除了p2-p5
var resultParam=新的SqlParameter
{
ParameterName=“结果”,
DbType=System.Data.DbType.Boolean,
方向=System.Data.ParameterDirection.Output
};
var sql=“EXEC sp_MyProcedure@p1、@p2、@p3、@p4、@p5、@Result OUTPUT”;
_=wait DbContext.Database.ExecuteSqlRawAsync(sql、p1、p2、p3、p4、p5、resultParam);
返回(bool)resultParam.Value;
}
}

在单元测试中,您可以假设其他部件和外部调用总是正常执行。所以你可以像这样模拟通话。其中一种方法是通过将过程调用移动到具有特定实现的接口中来实现某些策略

interface IProcedureExecutor
{
    void MyProcedure();
}

class DefaultProcedureExecutor: IProcedureExecutor
{
    public void MyProcedure()
    {
        //call procedure in database
    }
}

class MockedProcedureExecutor: IProcedureExecutor
{
    public void MyProcedure()
    {
        //do some direct data operations with in memory database
    }
}
然后在单元测试期间,在
MyFoo
中传递
iprocedurexecutor
,并用模拟实例替换实例


这只是一个例子。你也可以模仿这样的任何部分

我的最终解决方案是基于斯塔斯·彼得罗夫给出的答案。我使用带有类的接口包装了对
DbContext.Database.ExecuteSqlRawAsync()
的调用,该类在
Startup.ConfigureServices()
中添加到DI中

我创建了以下接口和类:

公共接口IStoredProcedureExecutor
{
公共任务ExecuteSqlRawAsync(字符串sql,参数对象[]参数);
}
公共类StoredProcedureExecutor:HistoredProceExecutor
{
公共应用程序DbContext DbContext{get;}
公共存储过程执行器(ApplicationDbContext dbContext)
{
DbContext=DbContext;
}
公共任务ExecuteSqlRawAsync(字符串sql,参数对象[]参数)
{
返回DbContext.Database.ExecuteSqlRawAsync(sql,参数);
}
}
在问题的代码中,我替换了此调用:

\uuwait DbContext.Database.ExecuteSqlRawAsync(sql、p1、p2、p3、p4、p5、resultParam);
为此:

\uuU4=等待StoredProcedureExecutor.ExecuteSqlRawAsync(sql、p1、p2、p3、p4、p5、resultParam);
然后在测试代码中,我创建了这个我实例化的类,设置了一个合适的
ReturnValue
,然后插入到我正在测试的类中,而不是
StoredProcedureExecutor

类TestStoredProcedureExecutor:IStoredProcedureExecutor
{
public bool ReturnValue{get;set;}
公共任务ExecuteSqlRawAsync(字符串sql,参数对象[]参数)
{
foreach(参数中的var参数)
{
var p=(SqlParameter)param;
如果(p.Direction==System.Data.ParameterDirection.Output)p.Value=ReturnValue;
}
返回Task.FromResult(0);
}
}

感谢您的回复。如果理解正确,我会使用
DefaultProcedureExecutor.MyProcedure()
将包含
ExecuteSqlRawAsync
的行包装起来,并让代码调用后者。我希望以某种方式模拟DbContext.Database.ExecuteSqlRawAsync()函数,但如果我做不到,那么我将尝试您的建议。再次感谢。我根据你的建议使用了一种解决方案。我将发布我的解决方案作为一个单独的答案。另外,您可以尝试使用Moq lib并执行类似于
var mock=new mock()
的操作,然后执行
mock.Setup(=>\uu.ExecuteSqlRawAsync())。返回(…一些结果…
,然后作为
mock.object
传递给您的服务模拟对象