I am writing some unit tests for Database creation using EF codefirst.
During execution of Unit tests, the DBContext->OnModelCreating method is executed only 1 time, and the model is then cached for the rest of the remaining tests.
I want to be able to execute the "OnModelCreating" for each unit test separately, by trying to set the ModelCaching property, like specified in the Documentation:
// Remarks:
// Typically, this method is called only once when the first instance of a derived
// context is created. The model for that context is then cached and is for all
// further instances of the context in the app domain. This caching can be disabled
// by setting the ModelCaching property on the given ModelBuidler, but note that
// this can seriously degrade performance. More control over caching is provided
// through use of the DbModelBuilder and DbContextFactory classes directly.
protected virtual void OnModelCreating(DbModelBuilder modelBuilder);
However, there is no such Property "ModelCaching" on this modelbuilder.
How else can I disable this model caching? Tests are running fine one by one, but because of this caching they are failing when running in a row.
Better said, how can I force the ApplicationDbContext -> OnModelCreating to be run for each test individually? Now it is run only once, when it is first used for a bunch of Unit tests.
Seems like this property is not available anymore. You need to keep different DB models and initialize your context for different connection.
This answer helped me in my case. By implementing the IDbModelCacheKeyProvider EF can cache multiple DB model for you based on the different CacheKey.
Related
I have someone else's ASP.NET application that uses code-first EF and comes with the code for database migration and seed. I usually run migration.exe before the new version of the app gets installed and it worked well as long as I let EF do everything starting from creating a brand new database. Now I have to make it work against an existing and EMPTY database. When I run migration.exe it doesn't return any errors and perform all the required migration steps but it not longer runs seed step (it probably assumes it's not needed since the database already exists). How do I force running seed step in this case?
There are steps you need to ensure are set for your database initialization to achieve what you want:
CreateDatabaseIfNotExists: This is default initializer. As the name suggests, it will create the database if none exists as per the configuration. However, if you change the model class and then run the application with this initializer, then it will throw an exception.
DropCreateDatabaseIfModelChanges: This initializer drops an existing database and creates a new database, if your model classes (entity classes) have been changed. So you don't have to worry about maintaining your database schema, when your model classes change.
DropCreateDatabaseAlways: As the name suggests, this initializer drops an existing database every time you run the application, irrespective of whether your model classes have changed or not. This will be useful, when you want fresh database, every time you run the application, like while you are developing the application.
Custom DB Initializer: You can also create your own custom initializer, if any of the above doesn't satisfy your requirements or you want to do some other process that initializes the database using the above initializer.
The best help guide I followed with getting started on understanding Code first was written here and references the part you are referring to:
http://www.entityframeworktutorial.net/code-first/database-initialization-strategy-in-code-first.aspx
The important thing though is to know that you may want different things for different environments too and may wish to get into understanding how to do different deployments for different environments.
An example of a custom one would be like this:
public class EasyContext : DbContext
{
public EasyContext() : base("name=EasyEntity")
{
Database.SetInitializer<EasyContext>(new EasyInitializer());
}
public DbSet<Person> Person { get; set; }
}
You should be setting a breakpoint to make sure it is being caught or else you can force a new configuration to fire as well.
Using xUnit and the TestServer from Microsoft.AspNet.TestHost, how can I wrap each test in a database transaction that can be rolled back after the test?
Here's how I create the TestServer:
TestServer = new TestServer(TestServer.CreateBuilder()
.UseStartup<Startup>());
The Startup that's referenced there is the Startup from the web app project. In the ConfigureServices method in that Startup class I add EF like this:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TrailsDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
I could pull the DbContext back of services and store a static reference on the Startup class, but that seems pretty hacky. Is there any way I can instantiate the DbContext where I create the TestServer and somehow have the web app use that instead of the one in the Startup class?
Edit: I have tried instantiating another instance of the DbContext where I create the TestServer and using that context to delete and recreate the database before each test, but that adds about 10 seconds to each test's run time.
Some advice: the simplest approach would be to destroy the test database at the end and recreate for each test run. This ensures no lingering test-to-test contamination.
But since you asked how, this can be done by extending Xunit. Xunit allows you to define custom test cases and test runners. A complete answer is hard to include in a SO answer. The simplest solution uses ambient transactions. (Danger! Ambient transactions can be tricky.) Xunit has a sample for a custom BeforeAfterTestAttribute that rolls back a transaction. https://github.com/xunit/samples.xunit/tree/master/AutoRollbackExample. To use ambient transactions, turn off the default EF setting that throws if ambient transactions are present.(optionsBuilder.UseSqlServer().SuppressAmbientTransactionWarning()).
A more complicated, but better solution is to override XunitTestCaseRunner and inject a transaction into each test case, ensuring to rollback at the conclusion of each test.
Also, EF docs provides a sample of using the InMemory provider to test. You may find this useful.
"Testing In Memory : EF Core Docs"
I wish to unit test my business logic is loading the correct data by loading an entity via the business logic and comparing it to an entity loaded directly from the dbcontext.
Assert.AreEqual fails I'm guessing because the entities are loaded as tracked.
I thought that I could possibly use AsNoTracking(), but it didn't work.
Is there a way of "unwrapping" the entity from entity framework to a POCO?
I've read about disabling proxycreation, but is this the only option?
I'm hoping there is something similar (although I realise a completely different concept), to ko.utils.unwrapObservable() in the knockout javascript library.
It is strange integration test (it is not unit test at all because it uses database) - it should be enough to simply define static expectation instead of loading it again from the database. Dynamic tests are more error prone and can hide issues.
To make it work you must override Equal to compare data not references. Disabling proxy creation will not work because you will still have different reference from your business logic and different reference from tested context (unless you share the context but in such case the test will be even more strange).
I am developing an ASP MVC application using Entity Framework. I was thinking of writing code to cache the object returned by ModelBuilder (as is recommended by several sources), but then I ran into this on Scott Gu's blog:
"The OnModelCreating method above will be called the first time our NerdDinners class is used within a running application, and it is passed a “ModelBuilder” object as an argument. The ModelBuilder object can be used to customize the database persistence mapping rules of our model objects. We’ll look at some examples of how to do this below.
"EF only calls the “OnModelCreating” method once within a running application – and then automatically caches the ModelBuilder results. This avoids the performance hit of model creation each time a NerdDinners class is instantiated, and means that you don’t have to write any custom caching logic to get great performance within your applications."
Does this mean that EF automatically caches the ModelBuilder object, and I don't have to write code to do it, or is this something that is only done if the OnModelCreating method is overridden, or ... ??
From the Entity Framework Blog regarding performance improvements in EF 4
Model Caching
There is some cost involved in
discovering the model, processing Data
Annotations and applying fluent API
configuration. To avoid incurring this
cost every time a derived DbContext is
instantiated the model is cached
during the first initialization. The
cached model is then re-used each time
the same derived context is
constructed in the same AppDomain.
Model caching can be turned off by
setting the CacheForContextType
property on ModelBuilder to ‘false’ in
the OnModelCreating method.
So the answer is yes for Entity Framework 4.0
If you have a system that has multiple types of object contexts. For Eg: BillingObjectContext, HumanResourceObjectContext etc. All derive from ObjectContext but ObjectContext Class does not implement any specific interface like IObjectContext. How would you apply DI/IoC in case of multiple types of ObjectContext say using Ninject?
If you must depend on it in a test, you have to mock it. Here's a sample; it's not much harder than implementing an interface. See also TDD improvements in EF 4.
Why can't we just create the actual context object to be used in our tests? Since we don't want our tests to affect the production database, we can always specify a connection string that points to a test database. Before running each test, construct a new context, add the data you will need in your test, proceed with the unit test, then in the test cleanup section, delete all the records that were created during the test. The only side-affect here would be that the auto-increment IDs would be used up in the test database, but since it's a test database - who cares?
I know that most answers regarding this question propose using DI/IoC designs to create interfaces for data contexts etc. but the reason I am using Entity Framework is exactly to not write any interfaces for my database connections, object models, and simple CRUD transactions. To write mock interfaces for my data objects and to write complex queryable objects to support LINQ, defeats the purpose of relying on highly-tested and reliable Entity Framework.
This pattern for unit testing is not new - Ruby on Rails has been using it for a long time and it's worked out great. Just as .NET provides EF, RoR provides ActiveRecord objects and each unit test creates the objects it needs, proceeds with the tests, and then deletes all the constructed records.
How to specify connection string for test environment? Since all tests are in their own dedicated test project, adding a new App.Config file with a connection string for the test database would suffice.
Just think of how much headache and pain this will save you.
namespace ProjectNamespace
{
[TestClass]
public class UnitTest1
{
private ObjectContext objContext;
[TestInitialize]
public void SetUp()
{
// Create the object context and add all the necessary data records.
}
[TestMethod]
public void TestMethod1()
{
// Runs the tests.
}
[TestCleanup]
public void CleanUp()
{
// Delete created records.
}
}
}