I'm using EF6 and I'm now setting up some tests for my aggregates. I've decided to use Effort.EF6 because I'd like to have those tests run without having to install an entire database engine.
My DbContext uses migrations and a seeding method that inserts some data. Can Effort.EF6 make use of that or should I use Effort's methods of seeding data ?
The migrations take place automatically. I call the normal context seed method when I need populated data. Note that depending on the scope of your context (per test, or per test assembly) you may be running lots and lots of queries to do your seeding. That has both performance implications, and debugging issues, since any seeding bugs will start showing up as bugs in your tests, and any logging that happens during seeding will log as part of each test.
var connection = Effort.DbConnectionFactory.CreateTransient();
var context = new DbContext(connection);
context.Seed();
Related
We are using Entity Framework Core 2.2 with code first. Sometimes I change one of the entities, but forget to create a new migration, or I create a migration but only in one context (we have for different db engines). I want to check it automatically (ideally as NUnit test) so it runs in our CI server for every commit.
Manually I would try to create a new migration and check that created Up() and Down() methods are empty. It there any way to do it as a NUnit test?
Where is difficulty in creating a test that :
Creates a new DB
Applies all current migrations to create a schema
Tries to use all the entities. It can be as simple as adding an entity, querying that entity and deep-comparing they are the same.
Drops the DB
If the schema doesn't have a migration for new entity or change in entity, you are sure to get and SQL error out of this.
Sure, every time you create a new entity, you would need to add a new test. But that should be already happening if you are using TDD.
And speed shouldn't be a problem either, as creating and dropping a DB shouldn't take more than few seconds and there won't be many of these kind of tests. And they can be parallelized.
If you want to get fancy and don't want to write test for each entity, it could be possible to do something like this :
Use reflection to get all entity types supported by a Context.
Use auto data generator like Bogus or AutoFixture to fill the entities with data.
Round-trip the entities through DB.
Compare the original with retrieved using deep-comparer like Compare-Net-Objects.
The usefulness of such automated approach would depend on complexity of your data model. Would just work for simple model. But would require lots of tweaking and overrides if the model is complex.
Visual Studio 2017 Version 15.8.4
.NET Core 2.1
Microsoft.EntityFrameworkCore 2.1.3
According to the guidance I've read, data seeding is the method I should use to generate a migration that inserts data. EntityTypeBuilder.HasData is the method to use to ensure the migration contains the code to insert the data, and DbContext.OnModelCreating is the time to do it.
I have a large table I'm populating from a file in DbContext.OnModelCreating. I also have an NUnit test project which contains an integration test that executes code that touches the database. That test is currently failing, because instantiating my derived DbContext results in a call to OnModelCreating, which tries to open my large file and import it. Since my test project is creating my execution context, the relative path to my data file is different, so it can't find the file and it throws an exception.
That's not the problem. I can easily look for the file and return if it doesn't exist. But this exception draws my attention to the fact that OnModelCreating is always going to be called the first time I instantiate my derived DbContext class in an app domain, which means this entire table is going to exist in memory even though I haven't queried the database for its contents, which seems to defeat the purpose of having a database.
So, I can remove the the code in OnModelCreating that reads this file (and suddenly my tests pass), but if I do that, the next time I add a migration, the code in the Migration.Up will wipe out the contents of that table. Unless I erase the EntityTypeBuilder.HasData call for this table generated in the ModelSnapshot, which seems like a really dirty hack, and which I'll have to manually undo before performing a similar series of dirty hacks in the future any time I want to change the contents of this table.
So, the question is, how do I provide seed data for migrations without having that data automatically loaded into memory when I instantiate my DbContext and without manually editing the ModelSnapshot?
If I understand you correctly, you want to reset your DbContext to its initial state (w/o any tracked entities) after seed data is saved. If so, detach all entities tracked by the DbContext:
foreach( var entry in this.ChangeTracker.Entries() )
{
entry.State = EntityState.Detatched;
}
Edit:
To address the comment, seed data is only required for migrations. If you can use a different DbContext for your migrations, derive another DbContext from your current DbContext for use with your migrations and only include the seed data in that DbContext.
I'm trying to reduce the startup time for tests against an EF 6x datastore. The tests are within a transaction and the db gets rolled back once done. I would appreciate any suggestions on how to retain an instance of the DbContext between test sessions so that EF doesn't have to go through the whole view generation process again?
I don't want to use mocks/fakes, non-Microsoft branch of EF and interactive views are already in place. Thank you.
Different options. As you did not mentioned your aim of testing and there is not any code, the options are:
If you are inserting many records into your tables, you can do a bulk insert. The best library for doing this is:EntityFramework.BulkInsert-ef6. You can install it through Nuget console.
If you see slowness while working with data and you have many load/manipulation/save operations, you have to do in-memory operation as Sampath recommends.
If you are loading data, just load the columns that you need. You also should use lazy loading option(which from your post, I think you know it well).
4.Some portion of the slowness could be because of the architecture of your database. The key column types have a considerable effect on Where operations!
I would like to recommend you to use in-memory data for that. I am also used this pattern and it is really well and very fast. This is the pattern where the industry recommended and trouble free in long run. Always try to use best practices when you develop a software app.
When writing tests for your application it is often desirable to avoid
hitting the database. Entity Framework allows you to achieve this by
creating a context – with behavior defined by your tests – that makes
use of in-memory data.
Here is the article about how to do that :Testing with a mocking framework
Another article for you : Unit testing in C# using xUnit, Entity Framework, Effort and ASP.NET Boilerplate
With EF 4.1, you used to remove the IncludeMetadataConvention in order to prevent EF from querying for database metadata on every query.
In EF 5, I get a warning about IncludeMetadataConvention being obsolete, and in LinqPad, I can see that EF is now querying for migration history on every use. I'm working on a database first project (but using POCO's and DbContext). I don't want the overhead of these extra queries. How do I turn them off?
Update
I found that I can disable this on a per-dbContext basis by calling
System.Data.Entity.Database.SetInitializer<theDbContext>(null);
However, I would like to be able to disable initialization globally (Imagine a large app, and we want to ensure that we are not running these queries (and definitely not trying to create a database) when it is deployed for production.
If you need to do it for every context type in your large application you can create some code which will go through all of your assemblies, find all types derived from DbContext and invoke that call through reflection for every found type.
Btw. since EF 4.3 you can also change initializer from configuration but it is still per context basis because people usually don't have more than one.
Why not put the code in the constructor of your DbContext classes?
I do this as well as set a parameter to disable AutoDetectChangesEnabled, LazyLoading & Proxy CreationEnabled.
I'm writing unit-tests for an app that uses a database, and I'd like to be able to run the app against some sample/test data - but I'm not sure of the best way to setup the initial test data for the tests.
What I'm looking for is a means to run the code-under-test against the same database (or schematically identical) that I currently use while debugging - and before each test, I'd like to ensure that the database is reset to a clean slate prior to inserting the test data.
I realize that using an IRepository pattern would allow me to remove the complexity of testing against an actual database, but I'm not sure that will be possible in my case.
Any suggestions or articles that could point me in the right direction?
Thanks!
--EDIT--
Thanks everyone, those are some great suggestions! I'll probably go the route of mocking my data access layer, combined with some simple set-up classes to generate exactly the data I need per test.
Here's the general approach I try to use. I conceive of tests at about three or four levels:: unit-tests, interaction tests, integration tests, acceptance tests.
At the unit test level, it's just code. Any database interaction is mocked out, either manually or using one of the popular frameworks, so loading data is not an issue. They run quick, and make sure the objects work as expected. This allows for very quick write-test/write code/run test cycles. The mock objects serve up the data that is needed by each test.
Interaction tests test the interactions of non-trivial class interactions. Again, no database required, it's mocked out.
Now at the integration level, I'm testing integration of components, and that's where real databases, queues, services, yada yada, get thrown in. If I can, I'll use one of the popular in-memory databases, so initialization is not an issue. It always starts off empty, and I use utility classes to scrub the database and load exactly the data I want before each test, so that there's no coupling between the tests.
The problem I've hit using in-memory databases is that they often don't support all the features I need. For example, perhaps I require an outer join, and the in-memory DB doesn't support that. In that case, I'll typically test against a local conventional database such as MySQL, again, scrubbing it before each test. Since the app is deployed to production in a separate environment, that data is untouched by the testing cycle.
The best way I've found to handle this is to use a static test database with known data, and use transactions to ensure that your tests don't change anything.
In your test setup you would start a transaction, and in your test cleanup, you would roll the transaction back. This lets you modify data in your tests but also makes sure everything gets restored to its original state when the test completes.
I know you're using C# but in the Java World there's the Spring framework. It allows you to run database minipulations in a transaction and after this transaction, you roll this one back. This means that you operate against a real database without touching the state after the test finishes. Perhaps this could be a hint to further investigation in C#.
Mocking is of cause the best way to unit test your code.
As far as integration tests go, I have had some issues using in-memory databases like SQLite, mainly because of small differences in behaviour and/or syntax.
I have been using a local instance of MySql for integration tests in several projects. A returning problem is the server setup and creation of test data.
I have created a small Nuget package called Mysql.Server (see more at https://github.com/stumpdk/MySql.Server), that simply sets up a local instance of MySql every time you run your tests.
With this instance running you can easily set up table structures and sample data for your tests without being concerned of either your production environment or local server setup.
I don't think there is an easy way to finish this. You just have to create those Pre-Test sql setup scripts and post-test Tear-down scripts. Then you need trigger those scripts for each run. A lot of people suggest SQLLite for unit test setup.
I found it best to have my tests go to a different db so I could wipe it clean and put in the data I wanted for the test.
You may want to have the database be something that can be set within the program, then your test can tell the classes to change the database.
This code clears all data from all user's tables in MS SQL Server:
private DateTime _timeout;
public void ClearDatabase(SqlConnection connection)
{
_timeout = DateTime.Now + TimeSpan.FromSeconds(30);
do
{
SqlCommand command = connection.CreateCommand();
command.CommandText = "exec sp_MSforeachtable 'DELETE FROM ?'";
try
{
command.ExecuteNonQuery();
return;
}
catch (SqlException)
{
}
} while (!TimeOut());
if (TimeOut())
Assert.Fail("Fail to clear DB");
}
private bool TimeOut()
{
return DateTime.Now > _timeout;
}
If you are thinking about a real database usage, then mostlikely we're talking integration tests here. I.e tests, which check app behavior as a composition of different components contrary to unit tests, where components are supposed to be tested in isolation.
Having the testing scope defined, I wouldn't recommend using things like in-memory databases or mocking libraries as the other authors suggested. The problem is that usually there is a slightly different behavior or reduced set of features for in-memory databases and there is no database at all with mocking, therefore you'll be testing some other application in general sense and not the one you'll be delivering to your customers.
I'd rather suggest to minimize the amount of integration tests by covering just a crucial parts of your logic leaving the rest for unit testing, while using a real database with the setup as close to the production one as possible. Test runs could be too slow and a real pain if there are a lot of integration ones.
Also you might use some tricks to optimize the speed of your tests execution:
Split tests to Read and Write in regard to the data mutations they introduce and run the former ones in parallel and without any cleanup. (E.g HTTP GET requests are safe to be run in parallel if the system under test is a webapp and tests are more like end-to-end);
Use the only insert/delete script for all the data and optimize as much as possible. You might find Reseed library I'm developing currently helpful. It's able to generate both insert and delete scripts for you. So basically what you asked for. Or check out Respawn which could be used for database cleanup;
Use database snapshots for the restore, which might be faster than full insert/delete cycle;
Wrap each test in transaction and revert it afterwards (this one is also not 100% honest and somewhat fragile);
Parallelize your tests by using a pool of databases instead of the only. Docker and TestContainers could be suitable here;