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

Rob*_*Rob 5 stored-procedures xunit.net entity-framework-core asp.net-core asp.net-core-3.1

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

我正在使用DbContextwith.UseInMemoryDatabase()进行其他测试,但内存数据库不能与存储过程一起使用。

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

如何对该功能进行单元测试?

public class MyFoo : IFoo
{
    public ApplicationDbContext DbContext { get; }

    public MyFoo(ApplicationDbContext dbContext)
    {
        DbContext = dbContext;
    }

    public async Task<bool> GetMyStoredProcResult(string val1, string val2, string val3, string val4, string val5)
    {
        // input validation removed for brevity
        var p1 = new SqlParameter
        {
            ParameterName = "p1",
            DbType = System.Data.DbType.String,
            Direction = System.Data.ParameterDirection.Input,
            Value = val1
        };
        // p2 - p5 removed for brevity
        var resultParam = new SqlParameter
        {
            ParameterName = "Result",
            DbType = System.Data.DbType.Boolean,
            Direction = System.Data.ParameterDirection.Output
        };
        var sql = "EXEC sp_MyProcedure @p1, @p2, @p3, @p4, @p5, @Result OUTPUT";
        _ = await DbContext.Database.ExecuteSqlRawAsync(sql, p1, p2, p3, p4, p5, resultParam);
        return (bool)resultParam.Value;
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 5

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

我创建了以下接口和类:

public interface IStoredProcedureExecutor
{
    public Task<int> ExecuteSqlRawAsync(string sql, params object[] parameters);
}

public class StoredProcedureExecutor : IStoredProcedureExecutor
{
    public ApplicationDbContext DbContext { get; }

    public StoredProcedureExecutor(ApplicationDbContext dbContext)
    {
        DbContext = dbContext;
    }

    public Task<int> ExecuteSqlRawAsync(string sql, params object[] parameters)
    {
        return DbContext.Database.ExecuteSqlRawAsync(sql, parameters);
    }
}
Run Code Online (Sandbox Code Playgroud)

在问题的代码中,我替换了这个调用:

_ = await DbContext.Database.ExecuteSqlRawAsync(sql, p1, p2, p3, p4, p5, resultParam);
Run Code Online (Sandbox Code Playgroud)

有了这个:

_ = await StoredProcedureExecutor.ExecuteSqlRawAsync(sql, p1, p2, p3, p4, p5, resultParam);
Run Code Online (Sandbox Code Playgroud)

然后在测试代码中,我创建了实例化的类,设置了合适的ReturnValue,然后插入到我正在测试的类中,而不是StoredProcedureExecutor

class TestStoredProcedureExecutor : IStoredProcedureExecutor
{
    public bool ReturnValue { get; set; }

    public Task<int> ExecuteSqlRawAsync(string sql, params object[] parameters)
    {
        foreach (var param in parameters)
        {
            var p = (SqlParameter)param;
            if (p.Direction == System.Data.ParameterDirection.Output) p.Value = ReturnValue;
        }
        return Task.FromResult(0);
    }
}
Run Code Online (Sandbox Code Playgroud)