- 11 minutes to read
As discussed in the Overview, a basic decision you need to make is whether your tests will involve your production database system - just as your application does - or whether your tests will run against a test double, which replaces your production database system.
Testing against a real external resource - rather than replacing it with a test double - can involve the following difficulties:
- In many cases, it's simply not possible or practical to test against the actual external resource. For example, your application may interact with some service that cannot be easily tested against (because of rate limiting, or the lack of a testing environment).
- Even when it's possible to involve the real external resource, this may be exceedingly slow: running a large amount of tests against a cloud service may cause tests to take too long. Testing should be part of the developer's everyday workflow, so it's important that tests run quickly.
- Executing tests against an external resource may involve isolation issues, where tests interfere with one another. For example, multiple tests running in parallel against a database may modify data and cause each other to fail in various ways. Using a test double avoids this, as each test runs against its own, in-memory resource, and is therefore naturally isolated from other tests.
However, tests which pass against a test double don't guarantee that your program works when running against the real external resource. For example, a database test double may perform case-sensitive string comparisons, whereas the production database system does case-insensitive comparisons. Such issues are only uncovered when tests are executed against your real production database, making these tests an important part of any testing strategy.
Testing against the database may be easier than it seems
Because of the above difficulties with testing against a real database, developers are frequently urged to use test doubles first, and have a robust test suite which they can run frequently on their machines; tests involving the database, in contrast, are supposed to be executed much less frequently, and in many cases also provide far less coverage. We recommend giving more thought to the latter, and suggest that databases may actually be far less affected by the above problems than people tend to think:
- Most databases can nowadays be easily installed on the developer's machine. Container-based technologies such as Docker can make this very easy, and technologies such as Github Workspaces and Dev Container set up your entire development environment for you (including the database). When using SQL Server, it's also possible to test against LocalDB on Windows, or easily set up a Docker image on Linux.
- Testing against a local database - with a reasonable test dataset - is usually extremely fast: communication is completely local, and test data is typically buffered in memory on the database side. EF Core itself contains over 30,000 tests against SQL Server alone; these complete reliably in a few minutes, execute in CI on every single commit, and are very frequently executed by developers locally. Some developers turn to an in-memory database (a "fake") in the belief that this is needed for speed - this is almost never actually the case.
- Isolation is indeed a hurdle when running tests against a real database, as tests may modify data and interfere with one another. However, there are various techniques to provide isolation in database testing scenarios; we concentrate on these in Testing against your production database system).
The above is not meant to disparage test doubles or to argue against using them. For one thing, test doubles are necessary for some scenarios which cannot be tested otherwise, such as simulating database failure. However, in our experience, users frequently shy away from testing against their database for the above reasons, believing it's slow, hard or unreliable, when that isn't necessarily the case. Testing against your production database system aims to address this, providing guidelines and samples for writing fast, isolated tests against your database.
Different types of test doubles
Test doubles is a broad term which encompasses very different approaches. This section covers some common techniques involving test doubles for testing EF Core applications:
- Use SQLite (in-memory mode) as a database fake, replacing your production database system.
- Use the EF Core in-memory provider as a database fake, replacing your production database system.
- Mock or stub out
- Introduce a repository layer between EF Core and your application code, and mock or stub that layer.
Below, we'll explore what each method means, and compare it with the others. We recommend reading through the different methods to gain a full understanding of each one. If you've decided to write tests which don't involve your production database system, than a repository layer is the only approach allowing the comprehensive and reliable stubbing/mocking of the data layer. However, that approach has a significant cost in terms of implementation and maintenance.
SQLite as a database fake
One possible testing approach is to swap your production database (e.g. SQL Server) with SQLite, effectively using it as a testing "fake". Aside from ease of setup, SQLite has an in-memory database feature which is especially useful for testing: each test is naturally isolated in its own in-memory database, and no actual files need to be managed.
However, before doing this, it's important to understand that in EF Core, different database providers behave differently - EF Core does not attempt to abstract every aspect of the underlying database system. Fundamentally, this means that testing against SQLite does not guarantee the same results as against SQL Server, or any other database. Here are some examples of possible behavioral differences:
- The same LINQ query may return different results on different providers. For example, SQL Server does case-insensitive string comparison by default, whereas SQLite is case-sensitive. This can make your tests pass against SQLite where they would fail against SQL Server (or vice versa).
- Some queries which work on SQL Server simply aren't supported on SQLite, because the exact SQL support in these two database differs.
- If your query happens to use a provider-specific method such as SQL Server's EF.Functions.DateDiffDay, that query will fail on SQLite, and cannot be tested.
- Raw SQL may work, or it may fail or return different results, depending on exactly what is being done. SQL dialects are different in many ways across databases.
Compared to running tests against your production database system, it's relatively easy to get started with SQLite, and so many users do. Unfortunately, the above limitations tend to eventually become problematic when testing EF Core applications, even if they don't seem to be at the beginning. As a result, we recommend either writing your tests against your real database, or if using a test double is an absolute necessity, taking onboard the cost of a repository pattern as discussed below.
For information on how to use SQLite for testing, see this section.
In-memory as a database fake
As an alternative to SQLite, EF Core also comes with an in-memory provider. Although this provider was originally designed to support internal testing of EF Core itself, some developers use it as a database fake when testing EF Core applications. Doing so is highly discouraged: as a database fake, in-memory has the same issues as SQLite (see above), but in addition has the following additional limitations:
- The in-memory provider generally supports less query types than the SQLite provider, since it isn't a relational database. More queries will fail or behave differently in comparison to your production database.
- Transactions are not supported.
- Raw SQL is completely unsupported. Compare this with SQLite, where it's possible to use raw SQL, as long as that SQL works in the same way on SQLite and your production database.
- The in-memory provider has not been optimized for performance, and will generally work slower than SQLite in in-memory mode (or even your production database system).
In summary, in-memory has all the disadvantages of SQLite, along with a few more - and offers no advantages in return. If you are looking for a simple, in-memory database fake, use SQLite instead of the in-memory provider; but consider using the repository pattern instead as described below.
For information on how to use in-memory for testing, see the see this section.
Mocking or stubbing DbContext and DbSet
This approach typically uses a mock framework to create a test double of
DbSet, and tests against those doubles. Mocking
DbContext can be a good approach for testing various non-query functionality, such as calls to Add or SaveChanges(), allowing you to verify that your code called them in write scenarios.
However, properly mocking
DbSet query functionality is not possible, since queries are expressed via LINQ operators, which are static extension method calls over
IQueryable. As a result, when some people talk about "mocking
DbSet", what they really mean is that they create a
DbSet backed by an in-memory collection, and then evaluate query operators against that collection in memory, just like a simple
IEnumerable. Rather than a mock, this is actually a sort of fake, where the in-memory collection replaces the the real database.
Since only the
DbSet itself is faked and the query is evaluated in-memory, this approach ends up being very similar to using the EF Core in-memory provider: both techniques execute query operators in .NET over an in-memory collection. As a result, this technique suffers from the same drawbacks as well: queries will behave differently (e.g. around case sensitivity) or will simply fail (e.g. because of provider-specific methods), raw SQL won't work and transactions will be ignored at best. As a result, this technique should generally be avoided for testing any query code.
The approaches above attempted to either swap EF Core's production database provider with a fake testing provider, or to create a
DbSet backed by an in-memory collection. These techniques are similar in that they still evaluate the program's LINQ queries - either in SQLite or in memory - and this is ultimately the source of the difficulties outlined above: a query designed to execute against a specific production database cannot reliably execute elsewhere without issues.
For a proper, reliable test double, consider introducing a repository layer which mediates between your application code and EF Core. The production implementation of the repository contains the actual LINQ queries and executes them via EF Core. In testing, the repository abstraction is directly stubbed or mocked without needing any actual LINQ queries, effectively removing EF Core from your testing stack altogether and allowing tests to focus on application code alone.
The following diagram compares the database fake approach (SQLite/in-memory) with the repository pattern:
Since LINQ queries are no longer part of testing, you can directly provide query results to your application. Put another way, the previous approaches roughly allow stubbing out query inputs (e.g. replacing SQL Server tables with in-memory ones), but then still execute the actual query operators in-memory. The repository pattern, in contrast, allows you to stub out query outputs directly, allowing for far more powerful and focused unit testing. Note that for this to work, your repository cannot expose any IQueryable-returning methods, as these once again cannot be stubbed out; IEnumerable should be returned instead.
However, since the repository pattern requires encapsulating each and every (testable) LINQ query in an IEnumerable-returning method, it imposes an additional architectural layer on your application, and can incur significant cost to implement and maintain. This cost should not be discounted when making a choice on how to test an application, especially given that tests against the real database are still likely to be needed for the queries exposed by the repository.
It's worth noting that repositories do have advantages outside of just testing. They ensure all data access code is concentrated in one place rather than being spread around the application, and if your application needs to support more than one database, then the repository abstraction can be very helpful for tweaking queries across providers.
For a sample showing testing with a repository, see this section.
The following table provides a quick, comparative view of the different testing techniques, and shows which functionality can be tested under which approach:
|Feature||In-memory||SQLite in-memory||Mock DbContext||Repository pattern||Testing against the database|
|Test double type||Fake||Fake||Fake||Mock/stub||Real, no double|
|Exact query behavior?||Depends||Depends||Depends||Yes||Yes|
|Can use LINQ anywhere in the application?||Yes||Yes||Yes||No*||Yes|
* All testable database LINQ queries must be encapsulated in IEnumerable-returning repository methods, in order to be stubbed/mocked.
- We recommend that developers have good test coverage of their application running against their actual production database system. This provides confidence that the application actually works in production, and with proper design, tests can execute reliably and quickly. Since these tests are required in any case, it's a good idea to start there, and if needed, add tests using test doubles later, as required.
- If you've decided to use a test double, we recommend implementing the repository pattern, which allows you to stub or mock out your data access layer above EF Core, rather than using a fake EF Core provider (Sqlite/in-memory) or by mocking
- If the repository pattern isn't a viable option for some reason, consider using SQLite in-memory databases.
- Avoid the in-memory provider for testing purposes - this is discouraged and only supported for legacy applications.
- Avoid mocking
DbSetfor querying purposes.
Keep using EF6 if the data access code is stable and not likely to evolve or need new features. Port to EF Core if the data access code is evolving or if the app needs new features only available in EF Core.How does EF core help unit testing? ›
However, when writing unit tests, it's enough to keep the database in memory. Entity Framework Core allows you to run on a virtual database created only in memory. We can also use the SQLite database because it works fast and does not need a server. It also has a mode in which it can run in memory.Should I use in-memory database for testing? ›
While some users use the in-memory database for testing, this is generally discouraged; the SQLite provider in in-memory mode is a more appropriate test replacement for relational databases.How do you use UseInMemoryDatabase? ›
Inject ApiContext to the CategoryController via constructor. Create a GET method which will call GetCategories of ApiContext and return the data. Configure ApiContext in the startup class with the option UseInMemoryDatabase. Now, run the application and you will be able to see all the added categories.Is EF core faster than EF6? ›
Entity Framework (EF) Core, Microsoft's object-to-database mapper library for . NET Framework, brings performance improvements for data updates in version 7, Microsoft claims. The performance of SaveChanges method in EF7 is up to 74% faster than in EF6, in some scenarios.What happened to .NET core? ›
Starting with the December 2022 servicing update for Visual Studio 2019 16.11, Visual Studio 2019 17.0, and Visual Studio 2022 17.2, the . NET Core 3.1 component in Visual Studio will be changed to out of support and optional.What is a DbContext? ›
A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit. DbContext is conceptually similar to ObjectContext.What is mock setup? ›
Setup method is used to set expectations on the mock object For example: mock. Setup(foo => foo.What is mock repository? ›
Mocking is a way to encapsulate your unit tests. If you want to test a service method you are not interested if the repository is working. For this you will write repository tests. Therefore you mock the repository call and tell which result should be returned to test your method in all possible situation.Should I mock database for testing? ›
Yes, absolutely! Because our code that talks to the real DB is already tested carefully in the previous lecture. So all we need to do is: make sure that the mock DB implements the same interface as the real DB. Then everything will be working just fine when being put together.
Mocking and stubbing are the cornerstones of having quick and simple unit tests. Mocks are useful if you have a dependency on an external system, file reading takes too long, the database connection is unreliable, or if you don't want to send an email after every test.Why H2 is not used in production? ›
Mainly, H2 database can be configured to run as inmemory database, which means that data will not persist on the disk. Because of embedded database it is not used for production development, but mostly used for development and testing.How do I use in memory database for Entity Framework Core? ›
- Install the EF Core InMemory NuGet package.
- Create a new custom DbContext class.
- Create the model classes.
- Create the Repository class.
- Add the Repository as a service to the container.
- Create a new controller.
Repository pattern is one of the preferred patterns to apply in an application because it allows programmers to integrate all of the fundamental data operations related to an entity in one main class. Without this pattern, developers would need to create multiple classes for each entity with the same logic.Is SQLite an in memory database? ›
An SQLite database is normally stored in a single ordinary disk file. However, in certain circumstances, the database might be stored in memory. The most common way to force an SQLite database to exist purely in memory is to open the database using the special filename ":memory:".How can I make my EF core faster? ›
- Use indexes properly.
- Project only properties you need.
- Limit the resultset size.
- Efficient pagination.
- Avoid cartesian explosion when loading related entities.
- Load related entities eagerly when possible.
- Buffering and streaming.
- Tracking, no-tracking and identity resolution.
We are going to talk about the reasons why Entity Framework is still relevant in modern application development and why you should consider using it when designing your next application, even if its performance is not up there with ADO.NET or Dapper. While EF is part of the ADO.NET, it has its own module in both .What can I use instead of Entity Framework? ›
- NHibernate. It is a mature, open source object-relational mapper for the . ...
- Entity Framework Core. It is a lightweight, extensible, open source and cross-platform version of the. ...
- Hibernate. ...
- SQLAlchemy. ...
- Sequelize. ...
- Dapper. ...
- Doctrine 2. ...
NET is dead as a future framework for web applications and software. Microsoft won't be building for it and they won't support it. But software that already runs on . NET and is no longer being updated will still run on it.Is .NET being phased out? ›
Note: The . NET 5.0 SDK versions will continue to be supported in VS 16.11 until December of 2022 when . NET Core 3.1 goes out of support so that . NET Core 3.1 customers can continue to use 16.11 to developer their applications.
NET Core Framework is slated to go out of support on December 13th, 2022. Microsoft recommends upgrading . NET Core 3.1 applications to . NET 6.0 to stay supported for the future, while the developers have mixed feelings about the .What is the difference between DbContext and DbSet? ›
Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database.What is difference between DbContext and ObjectContext? ›
DbContext is a lightweight version of the ObjectContext class, which is laid almost right on top of ObjectContext (there is even a way to get to the ObjectContext from just the DbContext).What is scaffold in .NET core? ›
Scaffolding in ASP.NET Core is a technique used to generate code at design time to support a number of common application scenarios when working with Entity Framework Core. The code generation tool is available as a Nuget package.What is stubbing in testing? ›
A stub is a small piece of code that takes the place of another component during testing. The benefit of using a stub is that it returns consistent results, making the test easier to write. And you can run tests even if the other components are not working yet.What is the difference between strict and loose mock? ›
When creating a mock, we can also give it strict or loose behavior. Strict behavior means that exceptions will be thrown if anything that was not set up on our interface is called. Loose behavior, on the other hand, does not throw exceptions in situations like this. Mocks, by default, are loose.What is callback mock? ›
Callbacks. A powerful capability of Moq is to attach custom code to configured methods and properties' getters and setters. This capability is often referred to as Callbacks. var mock = new Mock<IService>();How do you write a unit test in Moq? ›
- Ignore input parameter value.
- Throw exception.
- Setup Properties.
- Sequential calls.
CallBase , when initialized during a mock construction, is used to specify whether the base class virtual implementation will be invoked for mocked dependencies if no setup is matched. The default value is false . This is useful when mocking HTML/web controls of the System.How do you mock a method? ›
Mockito when() method
It should be used when we want to mock to return specific values when particular methods are called. In simple terms, "When the XYZ() method is called, then return ABC." It is mostly used when there is some condition to execute. Following code snippet shows how to use when() method: when(mock.
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.Does integration testing use mocks? ›
In integration testing, the rules are different from unit tests. Here, you should only test the implementation and functionality that you have the control to edit. Mocks and stubs can be used for this purpose.How do I unit test a database connection? ›
- Write a script that will wipe out the database before you start unit tests, then populate the database with a predefined set of data and run the tests. ...
- Ensure that the database is in a known state before each test runs, and verify the state after the run using assertions logic.
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behavior or state of external dependencies.Why do we use mocking in unit testing? ›
Mocking is a way to replace a dependency in a unit under test with a stand-in for that dependency. The stand-in allows the unit under test to be tested without invoking the real dependency.What's the difference between unit test and integration test? ›
Unit Testing is a kind of white box testing, whereas Integration Testing is a kind of black-box testing. For Unit Testing, accessibility of code is required, as it tests the written code, while for Integration Testing, access to code is not required, since it tests the interactions and interfaces between modules.Is H2 better than SQLite? ›
SQLite is much more robust to corruption. Speed wise H2 was much faster in my case. With SQLite transactions are particularly costly, so you should prefer doing bulk operations within transactions or via batches.Does H2 automatically create tables? ›
Error : Table is not created automatically in H2 embedded db or I'm unable to see the tables. Usually, the table's are created but the URL used in H2 GUI Console is wrong. In the browser, change the database URL to jdbc:h2:mem:testdb (Shown in the screen below). You should be good to go!Is H2 and hibernate same? ›
In this Hibernate H2 database tutorial, you will learn how to create a Hibernate Application to connect the H2 in-memory database. Hibernate is an object-relational mapping framework for the Java language. It provides a framework for mapping an object-oriented domain model to a relational database.What does memory database mean? ›
In-memory databases are purpose-built databases that rely primarily on memory for data storage, in contrast to databases that store data on disk or SSDs. In-memory data stores are designed to enable minimal response times by eliminating the need to access disks.
No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn't useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn't helpful.What is difference between dependency injection and repository pattern? ›
They really aren't comparable a repository is something you can inject via dependency injection. The aim of DI is to make your application loosely coupled. Rather than specify a concrete implementation you can specify an interface that defines a contract that an implementation must fulfil.Should I use repository pattern? ›
The Repository pattern makes it easier to test your application logic. The Repository pattern allows you to easily test your application with unit tests. Remember that unit tests only test your code, not infrastructure, so the repository abstractions make it easier to achieve that goal.Is SQLite faster than Redis? ›
SQLite is very fast, and you're only requiring one IO action (on the commit ). Redis is doing significantly more IO since it's over the network. A more apples-to-apples comparison would involve a relational database accessed over a network (like MySQL or PostgreSQL).What is the best in-memory database? ›
- Redis. Redis is a distributed cache and an in-memory database. ...
- SQLite. SQLite is a C module that supports a disk-based database that doesn't need a dedicated server program and may be accessed using a nonstandard SQL query language. ...
- Memgraph. ...
- Aerospike. ...
SQLite is competitive with, and usually faster than, blobs stored in separate files on disk, for both reading and writing.Can I use EF6 in .NET core? ›
To use Entity Framework 6, your project has to compile against . NET Framework, as Entity Framework 6 doesn't support . NET Core. If you need cross-platform features you will need to upgrade to Entity Framework Core.What is EF6 and EF core? ›
Entity Framework 6 (EF6) is an object-relational mapper designed for . NET Framework but with support for . NET Core. EF6 is a stable, supported product, but is no longer being actively developed.What OS does EF6 support? ›
x. This means that EF 6.3 is cross-platform and supported on other operating systems besides Windows, like Linux and macOS. The migrations commands have been rewritten to execute out of process and work with SDK-style projects.Can I use EF core in .NET framework? ›
You can use EF Core in APIs and applications that require the full . NET Framework, as well as those that target only the cross-platform .
Entity Framework Core is the new version of Entity Framework after EF 6. x. It is open-source, lightweight, extensible and a cross-platform version of Entity Framework data access technology. Entity Framework is an Object/Relational Mapping (O/RM) framework.How do I use EDMX in .NET core? ›
- Create a new Windows Form Application project:
- "EntityFrameWorkSample" >> "Add" >> "New Item" >> "Data" >> "ADO.NET Entity Data Model" ...
- Choose the model content. ...
- There is the Entity Connection. ...
- Choose the Categories table and select this one.
Code-First is mainly useful in Domain Driven Design. In the Code-First approach, you focus on the domain of your application and start creating classes for your domain entity rather than design your database first and then create the classes which match your database design.Is Entity Framework core better than Entity Framework? ›
Entity Framework (EF) Core was a complete rewrite from the tried and tested EF6. One of the most touted benefits EF Core has over EF6 is improved performance.Should I use EF core? ›
EF should be considered a great ORM framework which allows faster development, easier and quicker operations to the DB, as long as you are careful and know how it works in order to avoid certain mistakes and create performance problems.Is EF6 still supported? ›
Although Entity Framework 6. x is still supported, it is no longer being developed and will only receive fixes for security issues.What can I use instead of Entity Framework? ›
- NHibernate. It is a mature, open source object-relational mapper for the . ...
- Entity Framework Core. It is a lightweight, extensible, open source and cross-platform version of the. ...
- Hibernate. ...
- SQLAlchemy. ...
- Sequelize. ...
- Dapper. ...
- Doctrine 2. ...
Dapper is literally much faster than Entity Framework Core considering the fact that there are no bells and whistles in Dapper. It is a straight forward Micro ORM that has minimal features as well.Is Entity Framework an ORM? ›
Entity Framework (EF) is an open source object–relational mapping (ORM) framework for ADO.NET.Can Core 3.1 Use EF Core 5? ›
Technically, EF Core 5 can run on . NET Core 3.1, but aligning versions is always a good idea.
A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit. DbContext is conceptually similar to ObjectContext.Which version of Entity Framework should I use for .NET Core 5? ›
Overview of Entity Framework Core 5.0. Entity Framework Core is the new version of the Entity Framework after EF 6.0. EF Core is open-source, lightweight, extensible, and a cross-platform based version of Entity Framework Data access technology. Entity Framework is an Object/Relational Mapping (ORM) based framework.