Wanted Result: Run integration tests in parallel with entity framework configured to use some kind of database that enforces constraints.
Current Situation: I have a project with some integration tests, and by that I mean test set up with WebApplicationFactory<Startup>. Currently I use it with EF Core setup to use In Memory Database
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
ConfigureWebHostBuilder(builder);
builder.ConfigureTestServices(services =>
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("testDb");
options.UseInternalServiceProvider(serviceProvider);
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging();
});
});
}
While this works, the problem is that it doesn't enforce SQL queries and I already ran in multiple cases where integration tests show the feature works, but when trying to reproduce it with actual project running and having EF connected to SQL Server database, the feature fails because of some failed database constraint.
Ideas:
At first I thought of using SQLite in memory database. Didn't work, I think it's some concurrency issue, because at the start of every test case, I wipe the database with .EnsureDeleted() .EnsureCreated() .Migrate()
Then SQLite with files with random names. Didn't work, because I couldn't manage to wipe out internal DbContext Cache. I was sure that file was recreated and clean, but DbContext simply had cached entities.
Finally after some migrations I released that I wouldn't probably be able to use SQLite at all, because it doesn't support a lot of migration types (Removing FK, altering a column, etc.)/
Related
In the process of upgrading our version of Entity Framework from 2.2.6 to 6.0.12 many of the integration tests have broken. These use an in memory sqlite database.
The error revolves around migrations and a database function that was added in a migration. This function is used to define a Computed Column in the dbContext OnModelCreating() method. During unit test set up we run
_dbContext.Database.EnsureDeleted();
_dbContext.Database.EnsureCreated();
Calling EnsureCreated results in this exception being thrown:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'near "(": syntax error'.
Stack tracing this, it occurs OnModelCreating when we call.
modelBuilder.Entity<MyObject>()
.Property(p => p.LinkId)
.HasComputedColumnSql("Schema.fn_link_id([Id])");
The syntax for this appears to be correct, so despite the exception error message, this seems to be occurring because the function does not exist as EnsureCreated() does not run a migration.
So my question is what is the best method to update an in memory sqlite database to include a database function that was added during a migration and fix this error?
Things I've attempted:
Replacing the call to .EnsureCreated() with a call to .Migrate(). This doesn't work because of the limitations of SqLite migrations, being unable to AlterColumns etc. A work around exists where I could rewrite all of the existing migrations by hand to accommodate sqlite, but I'd like to avoid having to do this for 5+ years of migrations.
Use .Migrate() and also change the database provider to a local instance of SqlServer. This resolves the issue and the unit tests pass locally. However, this would result in additional overhead and considerations in our CICD pipeline which we'd like to avoid.
Create the function in the test itself to bypass a need for migrations - Results in the same unhelpful syntax error:
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
connection.CreateFunction("Schema.fn_link_id", (int Id) => Guid.NewGuid());
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlite(connection)
.Options;
Context
We have a series of development databases - Azure SQL - that we use for testing.
These databases are frequently dropped and created again.
We want to start using an Elastic Pool for a better managing of costs.
We're using Entity Framework Core to create and upgrade the databases with a Code First strategy.
There is the possibility of not wanting to use the pool for one or more of the environments, so this should be configurable, therefore, migrations do not seem like a viable choice.
Problem
So, i'm having trouble trying to provision an instance within an Elastic Pool, using EF Core.
Based on the info on this link, I tried the following:
modelBuilder.HasPerformanceLevelSql($"ELASTIC_POOL (name = [{_elasticPoolName}])");
This is being set inside OnModelCreating(ModelBuilder modelBuilder) of the DBContext.
Now, I would've expected the underlying T-SQL to be something like:
CREATE DATABASE [DatabaseSomething] ( SERVICE_OBJECTIVE = ELASTIC_POOL ( name = ElasticPoolSomething ) ) ;
However, having enabled a logger, and looking at the actual underlying generated T-SQL, I only see:
CREATE DATABASE [DatabaseSomething];
It's almost as if EF is ignoring the ELASTIC_POOL setting on the model builder.
Additional info
Having already failed a lot of tries against the actual Elastic Pool in our Azure, I am currently testing against a local SQL instance, which does not have an Elastic Pool, obviously, but i assume the generated T-SQL would still reflect the setting. Or am I wrong about this?
Also tried: modelBuilder.HasPerformanceLevelSql($"ELASTIC_POOL (name = {_elasticPoolName})"); (no square brackets)
Also tried: modelBuilder.HasPerformanceLevelSql($"SERVICE_OBJECTIVE = ELASTIC_POOL (name = [{_elasticPoolName}])");
Does anyone have an idea of something i might be missing? Or why this is happening?
Thanks in advance!
I'm currently writing xUnit test for a .net core application. This is the way I am setting up my DbContext in the Startup:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
Now I want the xUnit tests to be executed using SQLite, which would require me to be able to detect if the application is being executed normally or if the tests are being executed in my Startup Class. To then set up the DbContext depending on that.
Is there a way to do so? I have been googling a lot, but not been able to find a good solution.
Trying to understand what you want to do. I am not sure I got it but I can give some tips.
If you want to test the database in unit tests with EF you can do the following.
Create a context options builder object, using an in memory database
var options = new DbContextOptionsBuilder<YourContext>()
.UseInMemoryDatabase(databaseName: "TestingDb")
.Options;
Create a context using this options object
var context = new YourContext(options);
And create a repository using the above context.
_repository= new YourRepository(context);
Now use this new repository and you will have a proper in memory database for your tests. Using DbContextOptionsBuilder you can change the target database as you wish. I hope my answer helps you.
So, I'm trying to figure out what the golden path is for running an application against a PostgreSQL database in development and a SQL Server database in production. The difficult part is that the migrations will be different. Currently, my approach is like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(SetDbContextOptionsForEnvironment, ServiceLifetime.Transient);
}
private void SetDbContextOptionsForEnvironment(DbContextOptionsBuilder options)
{
if(_environmentName == "production") {
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
}
else {
options.UseNpgsql(Configuration["Data:DefaultConnection:ConnectionString"]);
}
}
Is the preferred way to keep the migrations in a separate assembly and specify that assembly in the options? So then I need to have multiple definitions of the same DbContext in those assemblies as well?
After thinking on this for quite a while, I believe that this is an anti-pattern. Regardless of my particular use case (postgres in dev, SQL Server in prod), I think it is frowned upon to use different database systems for different environments as there may be unexpected issues after deployment. Best to stick with the same one for development and production.
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();