集成测试共享数据库的多个实体框架dbcontexts [英] Integration testing multiple Entity framework dbcontexts that share a database

查看:116
本文介绍了集成测试共享数据库的多个实体框架dbcontexts的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我有多个小实体框架dbcontexts共享相同的数据库,例如:

  public class Context1: DbContext {
public Context1()
:base(DemoDb){
}
}

public class Context2:DbContext {
public Context2()
:base(DemoDb){
}
}

所有数据库更新都是通过脚本完成的,不依赖迁移(也不会前进)。问题是 - 你将如何对这些环境进行集成测试?



我相信这里有三个选择(可能还有更多我不知道) / p>

选项1 - 超级上下文 - 包含设置数据库所需的所有模型和配置的上下文:

  public class SuperContext:DbContext 
{
public SuperContext()
:base(DemoDb){
}
}

在此选项中,测试数据库将针对超级上下文设置,所有随后的测试将通过较小的上下文进行。
我不喜欢这个选项的原因是我将复制我已经构建的所有配置和实体模型。



选项2 - 创建一个用于集成测试的自定义初始化器,将运行所有相应的db初始化脚本:

  public class IntegrationTestInitializer :IDatabaseInitializer< DbContext> {

public void InitializeDatabase(DbContext context){
/ *运行脚本以在此设置数据库* /
}
}

此选项允许针对真实的数据库结构进行测试,但每次添加新的db脚本时也需要更新



选项3 - 只需测试各个上下文:



在此选项中,只需让EF创建测试数据库基于上下文,所有测试都将在自己的沙箱中运行。
我不喜欢这个的原因是它不觉得你会对数据库的真实表示进行测试。



m目前正在选择选项2.你们都想什么?有没有更好的方法?

解决方案

我使用集成测试很多,因为我仍然认为这是最可靠的涉及数据相关过程时的测试方式。我也有几个不同的上下文,以及用于数据库升级的DDL脚本,所以我们的情况非常相似。



我最后得到的是选项4 :通过常规用户界面维护单元测试数据库内容。当然,绝大多数集成测试暂时修改数据库内容,作为测试的行为阶段的一部分(更多关于这个临时),但内容是



这是为什么。



在某些阶段,我们还生成了数据库内容在测试会话开始时,可​​以通过代码或通过反序列化XML文件。 (我们还没有EF,但是否则我们可能在数据库初始化程序中可能已经有一些 Seed 方法)。渐渐地,我开始对这种做法感到疑虑。特别是当数据模型或业务逻辑发生变化时,维护代码/ XML是一项工作。当需要设计新的用例时。有时我允许自己对这些测试数据进行轻微的损坏,因为知道它不会影响测试。



此外,数据必须有意义,因为他们必须与来自实际应用的数据一样有效和一致。确保这一点的一种方法是通过应用程序本身生成数据,否则不可避免地会在种子方法中重复业务逻辑。 这是我发现的最重要的事情。测试不代表真实用例的数据星座不仅浪费时间,而且是错误的安全性。



所以我发现自己通过应用程序创建测试数据前端,然后精心地将此内容序列化为XML或编写将完全相同的代码。直到有一天,我发现我有这个数据库中的数据,所以为什么不直接使用它?



现在也许你会问如何使测试独立?



集成测试,就像单元测试一样,应该是可执行的。他们不应该依赖其他测试,也不应该受到他们的影响。我假设您的问题的背景是您为每个集成测试创建和种子数据库。这是实现独立测试的一种方式。



但是如果只有一个数据库,没有种子脚本呢?您可以恢复每个测试的备份。我们选择了不同的方法。每个集成测试都运行在$ code> TransactionScope 中,它始终未被提交。这很容易实现。每个测试工具从具有这些方法(NUnit)的基类继承:

  [SetUp] 
public void InitTestEnvironment ()
{
SetupTeardown.PerTestSetup();
}

[TearDown]
public void CleanTestEnvironment()
{
SetupTeardown.PerTestTearDown();
}

SetupTeardown

  public static void PerTestSetup()
{
_tranactionScope = new TransactionScope();


public static void PerTestTearDown()
{
if(_tranactionScope!= null)
{
_tranactionScope.Dispose(); //回滚测试中所做的任何更改。
_tranactionScope = null;
}
}

其中 _tranactionScope 是静态成员变量。


In my application I have multiple small entity framework dbcontexts which share the same database, for example:

public class Context1 : DbContext {
    public Context1()
        : base("DemoDb") {
    }
}

public class Context2 : DbContext {
    public Context2()
        : base("DemoDb") {
    }
}

All database updates are done via scripts and do not rely on migrations (nor will they going forward). The question is - how would you do integration testing against these contexts?

I believe there are three options here (there may be more I just don't know them)

Option 1 - Super context - a context which contains all models and configurations required for setting up the database:

public class SuperContext : DbContext
{
    public SuperContext()
        : base("DemoDb") {
    }
}

In this option the test database would be setup against the super context and all subsequent testing would be done through the smaller contexts. The reason I am not keen on this option is that I will be duplicating all the configurations and entity models that i have already built.

Option 2 - create a custom initialiser for integration tests that will run all the appropriate db initialisation scripts:

public class IntegrationTestInitializer : IDatabaseInitializer<DbContext> {

    public void InitializeDatabase(DbContext context) {
        /* run scripts to set up database here */
    }
}

This option allows for testing against the true database structure but will also require updating everytime new db scripts are added

Option 3 - just test the individual contexts:

In this option one would just let EF create the test database based upon the context and all tests would operate within there own "sandbox". The reason that I don't like this is that it doesn't feel like you would be testing against a true representation of the database.

I'm currently swaying towards options 2. What do you all think? Is there a better method out there?

解决方案

I'm using integration testing a lot, because I still think it's the most reliable way of testing when data-dependent processes are involved. I also have a couple of different contexts, and DDL scripts for database upgrades, so our situations are very similar.

What I ended up with was Option 4: maintaining unit test database content through the regular user interface. Of course most integration tests temporarily modify the database content, as part of the "act" phase of the test (more on this "temporary" later), but the content is not set up when the test session starts.

Here's why.

At some stage we also generated database content at the start of the test session, either by code or by deserializing XML files. (We didn't have EF yet, but otherwise we would probably have had some Seed method in a database initializer). Gradually I started to feel misgivings with this approach. It was a hell of a job to maintain the code/XML when the data model or the business logic changed, esp. when new use cases had to be devised. Sometimes I allowed myself a minor corruption of these test data, knowing that it would not affect the tests.

Also, the data had to make sense, as in they had to be as valid and coherent as data from the real application. One way to ensure that is to generate the data by the application itself, or else inevitably you will somehow duplicate business logic in the seed method. Mocking real-world data is actually very hard. That's the most important thing I found out. Testing data constellations that don't represent real use cases isn't only a wast of time, it's false security.

So I found myself creating the test data through the application's front end and then painstakingly serializing this content into XML or writing code that would generate exactly the same. Until one day it occurred to me that I had the data readily available in this database, so why not use it directly?

Now maybe you ask How to make tests independent?

Integration tests, just as unit tests, should be executable in isolation. They should not depend on other tests, nor should they be affected by them. I assume that the background of your question is that you create and seed a database for each integration test. This is one way to achieve independent tests.

But what if there is only one database, and no seed scripts? You could restore a backup for each test. We chose a different approach. Each integration test runs within a TransactionScope that's never committed. It is very easy to achieve this. Each test fixture inherits from a base class that has these methods (NUnit):

[SetUp]
public void InitTestEnvironment()
{
    SetupTeardown.PerTestSetup();
}

[TearDown]
public void CleanTestEnvironment()
{
    SetupTeardown.PerTestTearDown();
}

and in SetupTeardown:

public static void PerTestSetup()
{
    _tranactionScope = new TransactionScope();
}

public static void PerTestTearDown()
{
    if (_tranactionScope != null)
    {
        _tranactionScope.Dispose(); // Rollback any changes made in a test.
        _tranactionScope = null;
    }
}

where _tranactionScope is a static member variable.

这篇关于集成测试共享数据库的多个实体框架dbcontexts的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆