测试数据库调用 C#

Posted

技术标签:

【中文标题】测试数据库调用 C#【英文标题】:Testing database calls C# 【发布时间】:2014-11-16 14:44:47 【问题描述】:

首先,让我们在这里处理术语。我搜索的所有内容都说:“单元测试不会触及数据库!”我不想要单元测试。我想要一个测试,当我将数据发送到数据库时,我知道它正确地保存了它(以及测试其他 crud 操作)。我有一个存储库层,它基本上接受 DTO,然后将该 DTO 映射到实体框架模型,然后将该模型保存到数据库中。

我需要能够确保向这些方法发送 DTO 实际上是保存到数据库中。

存储库上的示例方法签名是:

public bool Save(SomeObjectDTO someObject)

我只需要测试这个方法调用是否返回true。

设置测试的最佳方法是什么?我调用的方法是保存到数据库的方法?

此外,是否有建立空白测试数据库的标准方法?如果当我点击“运行测试”时它会构建一个空数据库,用必要的初始数据填充它,然后执行所有 CRUD 操作(我所有的存储库调用)以查看它们都像他们一样保存,那就太好了应该是。

如果这个问题已经得到解答,我深表歉意,但是我搜索的所有内容要么有人说你不应该测试数据库调用,要么有人谈论在这里没有真正用处的模拟。

我只是想要一个关于如何设置这些类型的测试的示例和/或标准做法。

【问题讨论】:

我认为您必须自己编写。编写一个在你调用你的 repo 上的 CRUD 操作之后读取的类。测试数据实际上进入数据库的情况并不常见。大多数人进行测试以查看特定方法和调用是否返回所需的数据。 “不接触数据库”与你的单元测试是一个愚蠢的规则 IMO,并且论点是语义 - 如果你必须将数据库测试留给集成测试,但真正关心的是你的测试访问可能导致他们行为不可预测的共享资源(从而否定测试的好处)。 【参考方案1】:

您正在寻找的东西称为集成测试,它与编写单元测试一样重要。您的底层数据提供者暴露了许多潜在的错误,这些错误不一定会发现模拟您的存储库(无效的外键、标记为非空的东西的空数据等)。

我认为针对与生产系统相同的数据库提供程序进行测试也很重要,否则可能会丢失特定于实现的行为。我将 Azure SQL 用于一个项目,而不是创建内存中的 SQL CE 实例,我在 Azure 上有一个单独的数据库,仅用于我的集成测试。

如果您使用 XUnit(我确信它存在于其他测试框架中),有一个方便的属性 [AutoRollback] 会在每次测试运行后自动回滚您的事务。

[Fact]
[AutoRollback]
public void AddProductTest_AddsProductAndRetrievesItFromTheDatabase()

    // connect to your test db
    YourDbContext dbContext = new YourDbContext("TestConnectionString")

    dbContext.Products.Add(new Product(...));

    // get the recently added product (or whatever your query is)
    var result = dbContext.Single();

    // assert everything saved correctly
    Assert.Equals(...);

测试完成后,您的数据库将再次处于空白状态(或运行测试之前的任何状态)。

【讨论】:

【参考方案2】:

为了在使用 EntityFramework 时针对数据库进行测试,以下是我的滚动方式:

首先,如果需要,我使用ObjectContext 的工厂定义将访问ObjectContext 的类:在我的情况下,我在NT 服务中工作,因此上下文在请求期间不存在,或其他一些范围:YMMV,但如果您正在测试一个组件,您可以完全隔离工作而没有太多麻烦,因为您的 web 上下文工厂肯定会从请求中获取上下文:只是不要初始化/关闭它你的 DAL 课程。

public DataAccessClass: IWorkOnStuff

    public Func<DataEntities> DataAccessFactory  get; internal set; 

    private string ConnectionString;
    public PortailPatientManagerImplementation(string connectionString)
    
        ConnectionString = connectionString;
        DataAccessFactory = () =>  return new DataEntities(ConnectionString); ;
    

    /* interface methods */

    public IEnumerable<Stuff> GetTheStuff(SomeParameters params)
    
        using (var context = DataAccessFactory())
        
             return context.Stuff.Where(stuff => params.Match(stuff));
        
    

现在,有趣的是,当你想测试这个时,你可以使用a library called Effort,它可以让你在内存中映射一个数据库。为此,只需创建您的类,并在测试设置中告诉 Effort 从此处获取它:

public class TestDataAccessClass

    public DataAccessClass Target  get; set; 

    protected int Calls = 0;
    protected DataEntities DE;

    [SetUp]
    public void before_each_test()
    
        Target = new DataAccessClass(string.Empty);
        Calls = 0;
        FullAccessCalls = 0;

        var fakeConnection = "metadata=res://*/bla.csdl|res://*/bla.ssdl|res://*/bla.msl;provider=System.Data.SqlClient";

        DE = Effort.ObjectContextFactory.CreateTransient<DataEntities>(fakeConnection);
        Target.DataAccessFactory = () =>  Calls++; return DE; ;

        SetupSomeTestData(DE);
    


SetupSomeTestData 中只需添加您想要的实体(引用等),现在您可以调用您的方法来确保您的数据确实来自设置中定义的 ObjectContext。

有趣的是,正如 mfanto 所说,这是一个集成测试,不是单元测试,而是as he says it himself:

对我来说,这听起来不像是单元测试,而是集成测试!

你是对的,我在标题中使用了“单元测试”这个词,因为 搜索引擎优化的原因:) 而且大多数人似乎并不知道 它们之间的区别。

我不知道这是否是针对实体框架 DAL 进行测试的最佳方法;我花了一些时间来实现这个解决方案,我发现它并非没有优点,但我会关注这个问题,看看提出了哪些其他解决方案。

【讨论】:

以上是关于测试数据库调用 C#的主要内容,如果未能解决你的问题,请参考以下文章

c# winForm开发的一个异常问题,异常描述是:"调用的目标发生了异常问题"

请教:C# WebService调用Java WebService(返回Json类型数据) ,为啥接收为null

C#调用Rust dll测试

C#调用Rust dll测试

C# WinForm 窗体应用程序,调用 javascript 脚本

C# 单元测试 - JSON 上的断言