测试数据库调用C#

时间:2014-09-22 15:08:22

标签: c# database entity-framework testing

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

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

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

public bool Save(SomeObjectDTO someObject)

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

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

此外,是否有标准的方法来设置空白测试数据库?如果我点击"运行测试"那将是很好的。它构造一个空数据库,用必要的初始数据填充它,然后执行所有CRUD操作(我的所有存储库调用),看看它们都像它们一样保存。

我很抱歉,如果已经回答了这个问题,但我搜索的所有内容都有人说你不应该测试数据库调用,或者有人在谈论嘲笑这些在这里没有用的东西。

我只想了解如何设置这些类型的测试的示例和/或标准练习。

2 个答案:

答案 0 :(得分:2)

您正在寻找的内容称为集成测试,与编写单元测试同样重要。您的基础数据提供程序暴露了很多潜在的错误,模拟您的存储库不一定会找到(无效的外键,标记为非空的内容的空数据等)。

我认为您对与生产系统相同的数据库提供程序进行测试也很重要,否则存在缺少实现特定行为的风险。我将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(...);
}

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

答案 1 :(得分:1)

在使用EntityFramework时对数据库进行测试,以下是我的演绎方法:

首先,如果需要,我定义将使用ObjectContext工厂访问ObjectContext的类:在我的情况下,我在NT服务中工作,因此上下文没有'在请求或其他范围内生存:YMMV但是如果您正在测试组件,您可以完全隔离而不会有太多麻烦,因为您的工厂在Web中的上下文肯定会从请求中获取上下文:只需要#&# 39;在你的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

  

这听起来不像是单位,而是对我进行集成测试!

     

你是对的,我使用术语&#34;单元测试&#34;在标题中因为   SEO的原因:)大多数人似乎都不知道   他们之间的差异。

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