测试数据库调用C#

本文关键字:调用 数据库 测试数据 测试 | 更新日期: 2023-09-27 18:24:06

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

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

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

public bool Save(SomeObjectDTO someObject)

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

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

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

如果这个问题已经得到回答,我很抱歉,但我搜索到的所有内容要么有人说你不应该测试数据库调用,要么有人在谈论嘲笑,这在这里并没有真正的用处。

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

测试数据库调用C#

您正在寻找的是所谓的集成测试,它与编写单元测试一样重要。底层数据提供程序暴露了很多潜在的错误,嘲笑你的存储库不一定会发现这些错误(无效的外键、标记为非null的null数据等)。

我认为,针对与生产系统相同的数据库提供程序进行测试也很重要,否则会有丢失特定于实现的行为的风险。我在一个项目中使用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(...);
}

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

对于在使用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));
        }
    }
}

现在,有趣的是,当你想测试它时,你可以使用一个名为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所指出的,这是一个集成测试,而不是单元测试,但正如他自己所说:

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

你是对的,我在标题中使用"单元测试"一词是因为SEO原因:)而且大多数人似乎不知道它们之间的差异。

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