Testen von Code, der EF Core verwendetTesting code that uses EF Core

Das Testen von Code, der auf eine Datenbank zugreift, erfordert Folgendes:Testing code that accesses a database requires either:

  • Das Ausführen von Abfragen und Aktualisierungen in dem Datenbanksystem, das auch in der Produktion verwendet wird, oderRunning queries and updates against the same database system used in production.
  • Das Ausführen von Abfragen und Aktualisierungen in einem andere einfacher zu verwaltenden Datenbanksystem oderRunning queries and updates against some other easier to manage database system.
  • Das Verwenden von Testdoubles oder eines anderen Mechanismus, um die Verwendung einer Datenbank grundsätzlich zu vermeidenUsing test doubles or some other mechanism to avoid using a database at all.

In diesem Dokument werden die Abwägungen dargelegt, die mit jeder dieser Optionen verbunden sind, und es wird gezeigt, wie EF Core bei jedem Ansatz eingesetzt werden kann.This document outlines the trade-offs involved in each of these choices and shows how EF Core can be used with each approach.

Tipp

Informationen zu den hier vorgestellten Konzepten finden Sie unter EF Core-Testbeispiel.See EF Core testing sample for code demonstrating the concepts introduced here.

Alle Datenbankanbieter sind nicht gleichAll database providers are not equal

Es ist sehr wichtig zu verstehen, dass EF Core nicht darauf ausgelegt ist, jeden Aspekt des zugrunde liegenden Datenbanksystems zu abstrahieren.It is very important to understand that EF Core is not designed to abstract every aspect of the underlying database system. Stattdessen handelt es sich bei EF Core um einen einheitlichen Satz von Mustern und Konzepten, der bei jedem Datenbanksystem verwendet werden kann.Instead, EF Core is a common set of patterns and concepts that can be used with any database system. EF Core-Datenbankanbieter schichten dann datenbankspezifisches Verhalten und Funktionalität über diesem allgemeinen Framework.EF Core database providers then layer database-specific behavior and functionality over this common framework. Auf diese Weise kann jedes Datenbanksystem das tun, was es am besten kann, während gleichzeitig die Gemeinsamkeiten mit anderen Datenbanksystemen, falls angebracht, gewahrt bleiben.This allows each database system to do what it does best while still maintaining commonality, where appropriate, with other database systems.

Grundsätzlich bedeutet dies, dass ein Austauschen des Datenbankanbieters das Verhalten von EF Core verändert, und es kann nicht erwartet werden, dass die Anwendung ordnungsgemäß funktioniert, wenn sie nicht explizit alle Unterschiede im Verhalten berücksichtigt.Fundamentally, this means that switching out the database provider will change EF Core behavior and the application can't be expected to function correctly unless it explicitly accounts for any differences in behavior. Dennoch wird dies in vielen Fällen funktionieren, weil es ein hohes Maß an Gemeinsamkeiten unter relationalen Datenbanken gibt.That being said, in many cases doing this will work because there is a high degree of commonality amongst relational databases. Das ist gut und schlecht zugleich.This is good and bad. Gut, da das Wechseln zwischen Datenbanksystemen relativ einfach erfolgen kann.Good because moving between database systems can be relatively easy. Schlecht, weil es ein falsches Gefühl der Sicherheit vermitteln kann, wenn die Anwendung nicht vollständig mit dem neuen Datenbanksystem getestet wird.Bad because it can give a false sense of security if the application is not fully tested against the new database system.

Ansatz 1: Produktions-DatenbanksystemApproach 1: Production database system

Wie im vorigen Abschnitt beschrieben, ist die einzige Möglichkeit, sicher zu sein, dass das getestet wird, was in der Produktion ausgeführt wird, die Verwendung desselben Datenbanksystems.As described in the previous section, the only way to be sure you are testing what runs in production is to use the same database system. Wenn die bereitgestellte Anwendung beispielsweise SQL Azure verwendet, müssen die Tests auch mit SQL Azure erfolgen.For example, if the deployed application uses SQL Azure, then testing should also be done against SQL Azure.

Allerdings wäre es sowohl langsam als auch teuer, wenn jeder Entwickler Tests mit SQL Azure durchführen würde, während er aktiv am Code arbeitet.However, having every developer run tests against SQL Azure while actively working on the code would be both slow and expensive. Dies veranschaulicht den wesentlichen Haken bei diesen Ansätzen: Wann ist es angebracht, vom Produktions-Datenbanksystem abzuweichen, um die Testeffizienz zu verbessern?This illustrates the main trade-off involved throughout these approaches: when is it appropriate to deviate from the production database system so as to improve test efficiency?

Glücklicherweise ist die Antwort in diesem Fall recht einfach, nämlich das Verwenden einer lokal SQL Server-Instanz für Entwicklertests.Luckily, in this case the answer is quite easy: use local or on-premises SQL Server for developer testing. SQL Azure und SQL Server sind sehr ähnlich, sodass das Testen mit SQL Server in der Regel ein angemessener Kompromiss ist.SQL Azure and SQL Server are extremely similar, so testing against SQL Server is usually a reasonable trade-off. Dennoch ist es ratsam, vor dem Aufnehmen der Produktion Tests mit SQL Azure selbst durchzuführen.That being said, it is still wise to run tests against SQL Azure itself before going into production.

LocalDBLocalDB

Alle wichtigen Datenbanksysteme verfügen über eine Form von „Developer Edition“ für lokales Testen.All the major database systems have some form of "Developer Edition" for local testing. Bei SQL Server heißt dieses Feature LocalDB.SQL Server also has a feature called LocalDB. Der Hauptvorteil von LocalDB besteht darin, dass die Datenbankinstanz bei Bedarf hochgefahren wird.The primary advantage of LocalDB is that it spins up the database instance on demand. Dadurch wird vermieden, dass ein Datenbankdienst auf Ihrem Computer läuft, auch wenn gerade keine Tests ausgeführt werden.This avoids having a database service running on your machine even when you're not running tests.

LocalDB ist nicht ohne Probleme:LocalDB is not without its issues:

  • Sie unterstützt nicht alles, was von SQL Server Developer Edition unterstützt wird.It doesn't support everything that SQL Server Developer Edition does.
  • Sie ist unter Linux nicht verfügbar.It isn't available on Linux.
  • Sie kann beim ersten Testlauf Verzögerungen verursachen, wenn der Dienst hochgefahren wird.It can cause lag on first test run as the service is spun up.

Ich persönlich habe es nie als Problem empfunden, dass ein Datenbankdienst auf meinem Entwicklungscomputer läuft, und würde generell empfehlen, stattdessen die Developer Edition zu verwenden.Personally, I've never found it a problem having a database service running on my dev machine and I would generally recommend using Developer Edition instead. Allerdings kann LocalDB für einige Entwickler geeignet sein, insbesondere auf weniger leistungsfähigen Entwicklungscomputern.However, LocalDB may be appropriate for some people, especially on less powerful dev machines.

Das Ausführen von SQL Server (oder einem anderen Datenbanksystem) in einem Docker-Container (oder ähnlichem) ist eine andere Möglichkeit, das Datenbanksystem nicht direkt auf dem Entwicklungscomputer ausführen zu müssen.Running SQL Server (or any other database system) in a Docker container (or similar) is another way to avoid running the database system directly on your development machine.

Ansatz 2: SQLiteApproach 2: SQLite

EF Core testet den SQL Server-Anbieter hauptsächlich durch Ausführen in einer lokalen SQL Server-Instanz.EF Core tests the SQL Server provider primarily by running it against a local SQL Server instance. Bei diesen Tests werden in ein paar Minuten Zehntausende von Abfragen auf einem schnellen Computer ausgeführt.These tests run tens of thousands of queries in a couple of minutes on a fast machine. Dies veranschaulicht, dass die Verwendung des realen Datenbanksystems eine leistungsstarke Lösung sein kann.This illustrates that using the real database system can be a performant solution. Es ist ein Mythos, dass die Verwendung einer schlankeren Datenbank die einzige Möglichkeit ist, Tests schnell durchzuführen.It is a myth that using some lighter-weight database is the only way to run tests quickly.

Davon abgesehen, was ist, wenn Sie aus welchem Grund auch immer keine Tests mit etwas durchführen können, das Ihrem Produktions-Datenbanksystem nahe kommt?That being said, what if for whatever reason you can't run tests against something close to your production database system? Die nächstbeste Wahl ist die Verwendung von etwas mit ähnlicher Funktionalität.The next best choice is to use something with similar functionality. Dies bedeutet in der Regel eine andere relationale Datenbank, wofür SQLite die offensichtliche Wahl ist.This usually means another relational database, for which SQLite is the obvious choice.

SQLite ist aus folgenden Gründen eine gute Wahl:SQLite is a good choice because:

  • Sie wird In-Process mit Ihrer Anwendung ausgeführt und hat daher einen geringen Verarbeitungsaufwand.It runs in-process with your application and so has low overhead.
  • Sie verwendet einfache, automatisch erstellte Dateien für Datenbanken und erfordert daher keine Datenbankverwaltung.It uses simple, automatically created files for databases, and so doesn't require database management.
  • Sie hat einen InMemory-Modus, der sogar die Dateierstellung vermeidet.It has an in-memory mode that avoids even the file creation.

Beachten Sie jedoch Folgendes:However, remember that:

  • SQLite unterstützt nicht zwangsläufig alles, was Ihr Produktions-Datenbanksystem leistet.SQLite inevitably doesn't support everything that your production database system does.
  • SQLite verhält sich bei einigen Abfragen anders als Ihr Produktions-Datenbanksystem.SQLite will behave differently than your production database system for some queries.

Wenn Sie also SQLite für einige Tests verwenden, stellen Sie sicher, dass Sie die Test auch mit Ihrem tatsächlichen Datenbanksystem durchführen.So if you do use SQLite for some testing, make sure to also test against your real database system.

Unter Testen mit SQLite finden Sie eine EF Core-spezifische Anleitung.See Testing with SQLite for EF Core specific guidance.

Ansatz 3: Die InMemory-Datenbank von EF CoreApproach 3: The EF Core in-memory database

EF Core bietet eine InMemory-Datenbank, die wir für interne Tests von EF Core selbst verwenden.EF Core comes with an in-memory database that we use for internal testing of EF Core itself. Diese Datenbank ist im Allgemeinen nicht für das Testen von Anwendungen geeignet, die EF Core verwenden.This database is in general not suitable for testing applications that use EF Core. Dies gilt insbesondere in folgenden Fällen:Specifically:

  • Es handelt sich nicht um eine relationale Datenbank.It is not a relational database.
  • Transaktionen werden nicht unterstützt.It doesn't support transactions.
  • Unformatierte SQL-Abfragen können nicht ausgeführt werden.It cannot run raw SQL queries.
  • Sie ist nicht für Leistung optimiert.It is not optimized for performance.

Nichts davon ist beim Testen interner EF Core-Aspekte sehr wichtig, weil wir es speziell dort einsetzen, wo die Datenbank für den Test irrelevant ist.None of this is very important when testing EF Core internals because we use it specifically where the database is irrelevant to the test. Auf der anderen Seite sind diese Dinge beim Testen einer Anwendung, die EF Core verwendet, in der Regel sehr wichtig.On the other hand, these things tend to be very important when testing an application that uses EF Core.

KomponententestUnit testing

Erwägen Sie das Testen eines Teils der Geschäftslogik, der möglicherweise einige Daten aus einer Datenbank verwenden muss, aber nicht inhärent die Datenbankinteraktionen testet.Consider testing a piece of business logic that might need to use some data from a database, but is not inherently testing the database interactions. Eine Möglichkeit ist die Verwendung eines Testdoubles, z. B. eines simulierten oder nachgemachten Elements.One option is to use a test double such as a mock or fake.

Wir verwenden Testdoubles für interne Tests von EF Core.We use test doubles for internal testing of EF Core. Wir versuchen jedoch nie, DbContext oder IQueryable zu simulieren.However, we never try to mock DbContext or IQueryable. Dies ist schwierig, umständlich und instabil.Doing so is difficult, cumbersome, and fragile. Machen Sie es nicht.Don't do it.

Stattdessen verwenden wir die In-Memory-Datenbank von EF, wenn wir Komponententests mit Logik durchführen, die DbContext verwendet.Instead we use the EF in-memory database when unit testing something that uses DbContext. In diesem Fall ist die Verwendung der In-Memory-Datenbank von EF angemessen, da der Test nicht vom Datenbankverhalten abhängig ist.In this case using the EF in-memory database is appropriate because the test is not dependent on database behavior. Tun Sie dies bloß nicht, um tatsächliche Datenbankabfragen oder -aktualisierungen zu testen.Just don't do this to test actual database queries or updates.

Das EF Core-Testbeispiel veranschaulicht Tests mit der In-Memory-Datenbank von EF sowie SQL Server und SQLite.The EF Core testing sample demonstrates tests using the EF in-memory database, as well as SQL Server and SQLite.