简体   繁体   English

测试数据库调用C#

[英]Testing database calls C#

First, let's address terminology here. 首先,让我们在这里解决术语。 Everything I search for says, "Unit tests don't touch the database!" 我搜索的所有内容都说:“单元测试不会触及数据库!” I don't want a unit test. 我不想进行单元测试。 I want a test that when I send data to a database, I know that it correctly saves it (and testing of other crud operations). 我想要一个测试,当我将数据发送到数据库时,我知道它正确地保存了它(以及测试其他crud操作)。 I have a repository layer that essentially accepts a DTO, then maps that DTO to an entity framework model, then saves that model to the database. 我有一个基本上接受DTO的存储库层,然后将该DTO映射到实体框架模型,然后将该模型保存到数据库。

I need to be able to ensure that sending a DTO to these methods is in fact saving to the database. 我需要能够确保将DTO发送到这些方法实际上是保存到数据库中。

An example method signature on the repository is: 存储库上的示例方法签名是:

public bool Save(SomeObjectDTO someObject)

I just need to test against whether or not this method call returns true. 我只需要测试此方法调用是否返回true。

What is the best way to set up tests where my methods being called are ones that save to the database? 设置测试的最佳方法是什么,我的方法被调用是保存到数据库的方法?

Furthermore, is there a standard way to set up a blank testing database? 此外,是否有标准的方法来设置空白测试数据库? It would be great if when I hit "Run tests" it constructs an empty database, fills it with initial data that is necessary, and then performs all the CRUD operations (all of my repository calls) to see that they all are saving like they should be. 如果我点击“运行测试”它构建一个空数据库,用必要的初始数据填充它,然后执行所有CRUD操作(我的所有存储库调用),看看它们都像它们一样保存,那就太棒了应该。

I apologize if this is already answered, but everything I have searched either has someone saying you shouldn't be testing database calls, or people talking about mocking which is not really useful here. 如果已经回答了这个问题我很抱歉,但是我搜索到的所有内容都有人说你不应该测试数据库调用,或者人们在谈论嘲笑这些在这里真的没用。

I just want an example and/or the standard practice on how these types of tests should be set up. 我只想要一个关于如何设置这些类型的测试的示例和/或标准实践。

What you're looking for is called integration testing, and is just as important as writing unit tests. 您正在寻找的是集成测试,它与编写单元测试同样重要。 There's a lot of potential bugs that are exposed by your underlying data provider that mocking your repository won't necessarily find (invalid foreign keys, null data for something marked as not null, etc). 底层数据提供程序暴露了很多潜在的错误,模拟您的存储库不一定会找到(无效的外键,标记为非空的内容的空数据等)。

I think it's also important that you test against the same database provider as your production system, otherwise there's a risk of missing implementation specific behavior. 我认为对您的生产系统进行相同的数据库提供程序测试也很重要,否则可能会丢失特定于实现的行为。 I use Azure SQL for a project, and instead of creating an in-memory SQL CE instance, I have a separate database on Azure that's only used for my integration tests. 我将Azure SQL用于项目,而不是创建内存中的SQL CE实例,我在Azure上有一个单独的数据库,仅用于我的集成测试。

If you use XUnit (and I'm sure it exists for other testing frameworks), there's a handy attribute [AutoRollback] that will automatically roll back your transaction after each test runs. 如果你使用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(...);
}

After the test is finished, your database will be at a blank slate again (or whatever it was before you ran the test). 测试完成后,您的数据库将再次处于空白状态(或者在您运行测试之前的任何状态)。

For testing against a database when using EntityFramework, here is how I roll: 要在使用EntityFramework时对数据库进行测试,请按以下步骤操作:

First of all, I define the class that will access the ObjectContext with a factory for the ObjectContext if needed: in my case I work in a NT service, so the context doesn't live during a request, or some other scope: YMMV but if you are testing a component you could work in complete isolation without too much hassle since your factory for the context in the web would certainly fetch the context from the request: just don't initialize / close it in your DAL class. 首先,我定义将要访问的类ObjectContext一个工厂为ObjectContext如果需要的话:在我来说,我在NT服务工作,因此上下文不要求,或其他一些范围期间住:因人而异,但如果你正在测试一个组件,你可以完全隔离而不会有太多的麻烦,因为你在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));
        }
    }
}

Now, what's interesting is that when you want to test this, you can use a library called Effort , which lets you map a database in memory. 现在,有趣的是,当您想要测试它时,您可以使用名为Effort的库 ,它允许您在内存中映射数据库。 To do it, just create your class, and in the test setup tell Effort to take it from here: 要做到这一点,只需创建您的类,并在测试设置中告诉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);
    }

}

In the SetupSomeTestData just add the entities you want (references, etc) and now you can call your methods to ensure that your data do come from the ObjectContext as defined in your setup. SetupSomeTestData只需添加所需的实体(引用等),现在您可以调用方法以确保您的数据确实来自您在设置中定义的ObjectContext。

Funnily enough, just as mfanto notes, this is an integration test, not a unit test, but as he says it himself : 有趣的是,正如mfanto所说,这是一个集成测试,而不是单元测试,但他自己说

This does not sound like unit but integration testing for me! 这对我来说听起来不像单元而是集成测试!

You are right, I use the term "unit testing" in the title because of SEO reasons :) Also most people don't seem to know about the differences between them. 你是对的,我在标题中使用术语“单元测试”,因为SEO的原因:)同时大多数人似乎并不知道它们之间的差异。

I don't know if this is the best way to test against an Entity Framework DAL; 我不知道这是否是测试实体框架DAL的最佳方法; it took me some time to achieve this solution and I find it is not without merits, but I will be watching this question in order to see what other solutions are proposed. 我花了一些时间来实现这个解决方案,我发现它并非没有优点,但我会看到这个问题,以便了解其他解决方案的建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM