Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration testing multiple Entity framework dbcontexts that share a database

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?

like image 753
tully2003 Avatar asked Oct 08 '14 12:10

tully2003


People also ask

Do integration tests use database?

Integration tests focus on testing how separate parts of the program work together. In the context of applications using a database, integration tests usually require a database to be available and contain data that is convenient to the scenarios intended to be tested.

What is multiple dbcontext in Entity Framework?

Entity Framework - Multiple DbContext. In this chapter, we will be learning how to migrate changes into the database when there are multiple DbContext classes in the application. Multiple DbContext was first introduced in Entity Framework 6.0. Multiple context classes may belong to a single database or two different databases.

What is Entity Framework Code-first migrations?

The “Migrations” folder in a project with Entity Framework Core code-first migrations has a bunch of files that’ll take care of the creation of your model – but they’re tied to a certain database context, as illustrated by the [DbContext (typeof (ApplicationDbContext))] attribute.

How do I add a dbcontext in EF Core?

In EF Core, there is an extension method that allows you to add a DBContext, called AddDBContext. This is a really useful method, however, in some cases, you may find that it doesn’t work for you. Specifically, if you’re trying to inject a DBContext to use for unit testing, it doesn’t allow you to access the DBContext that you register.

How many dbcontexts do I Need?

Closed 2 years ago. My impression to date has been that a DbContext is meant to represent your database, and thus, if your application uses one database, you'd want only one DbContext. However, some colleagues want to break functional areas out into separate DbContext classes.


1 Answers

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.

like image 195
Gert Arnold Avatar answered Oct 26 '22 22:10

Gert Arnold