Testen mit SQLiteTesting with SQLite

SQLite ist einen in-Memory-Modus, der Ihnen ermöglicht, SQLite verwenden, um Tests für eine relationale Datenbank, ohne den Aufwand für die tatsächliche Datenbankvorgänge zu schreiben.SQLite has an in-memory mode that allows you to use SQLite to write tests against a relational database, without the overhead of actual database operations.

Tipp

Sie können anzeigen, dass dieser Artikel Beispiel auf GitHubYou can view this article's sample on GitHub

Beispiel-TestszenarioExample testing scenario

Betrachten Sie den folgenden Dienst, der Anwendungscode im Zusammenhang mit Blogs Eingriffe ermöglicht.Consider the following service that allows application code to perform some operations related to blogs. Er verwendet intern eine DbContext , die eine Verbindung mit einer SQL Server-Datenbank her.Internally it uses a DbContext that connects to a SQL Server database. Es wäre hilfreich zum Austauschen von diesem Kontext für die Verbindung mit einer in-Memory-SQLite-Datenbank, damit wir effiziente Tests für diesen Dienst schreiben, ohne den Code ändern, oder führen Sie einen Großteil der Arbeit, die zum Erstellen eines Tests können doppelte des Kontexts.It would be useful to swap this context to connect to an in-memory SQLite database so that we can write efficient tests for this service without having to modify the code, or do a lot of work to create a test double of the context.

using System.Collections.Generic;
using System.Linq;

namespace BusinessLogic
{
    public class BlogService
    {
        private BloggingContext _context;

        public BlogService(BloggingContext context)
        {
            _context = context;
        }

        public void Add(string url)
        {
            var blog = new Blog { Url = url };
            _context.Blogs.Add(blog);
            _context.SaveChanges();
        }

        public IEnumerable<Blog> Find(string term)
        {
            return _context.Blogs
                .Where(b => b.Url.Contains(term))
                .OrderBy(b => b.Url)
                .ToList();
        }
    }
}

Bereiten Sie den KontextGet your context ready

Vermeiden Sie die Konfiguration von zwei DatenbankanbieterAvoid configuring two database providers

In den Tests wirst du extern konfigurieren Sie den Kontext des InMemory-Anbieters verwenden.In your tests you are going to externally configure the context to use the InMemory provider. Wenn Sie einen Datenbankanbieter durch Außerkraftsetzen konfigurieren OnConfiguring in den Kontext, dann müssen Sie fügen Sie bedingte Code zum sicherstellen, dass Sie nur den Datenbankanbieter konfigurieren, wenn Sie noch nicht konfiguriert wurde.If you are configuring a database provider by overriding OnConfiguring in your context, then you need to add some conditional code to ensure that you only configure the database provider if one has not already been configured.

Tipp

Bei Verwendung von ASP.NET Core sollte dann nicht mit diesem Code erforderlich, da der Datenbankanbieter außerhalb des Kontexts (in "Startup.cs) konfiguriert ist.If you are using ASP.NET Core, then you should not need this code since your database provider is configured outside of the context (in Startup.cs).

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFProviders.InMemory;Trusted_Connection=True;ConnectRetryCount=0");
    }
}

Fügen Sie einen Konstruktor zu TestzweckenAdd a constructor for testing

Die einfachste Methode zum Aktivieren von Tests mit einer anderen Datenbank so ändern Sie den Kontext, um einen Konstruktor verfügbar machen, das akzeptiert, wird eine DbContextOptions<TContext>.The simplest way to enable testing against a different database is to modify your context to expose a constructor that accepts a DbContextOptions<TContext>.

public class BloggingContext : DbContext
{
    public BloggingContext()
    { }

    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    { }

Tipp

DbContextOptions<TContext>Gibt dem Kontext alle zugehörigen Einstellungen, z. B. welche Datenbank für die Verbindung.DbContextOptions<TContext> tells the context all of its settings, such as which database to connect to. Dies ist das gleiche Objekt, das erstellt wird, durch die OnConfiguring-Methode in Ihrem Kontext ausgeführt wird.This is the same object that is built by running the OnConfiguring method in your context.

Schreiben von testsWriting tests

Der Schlüssel für das Testen von mit diesem Anbieter ist die Fähigkeit, teilen Sie den Kontext SQLite verwenden und Steuern des Gültigkeitsbereichs der Datenbank im Arbeitsspeicher.The key to testing with this provider is the ability to tell the context to use SQLite, and control the scope of the in-memory database. Der Bereich der Datenbank wird gesteuert, durch Öffnen und schließen die Verbindung.The scope of the database is controlled by opening and closing the connection. Die Datenbank ist auf die Dauer beschränkt, die die Verbindung geöffnet ist.The database is scoped to the duration that the connection is open. Sie möchten in der Regel eine fehlerfreie Datenbank für jede Testmethode.Typically you want a clean database for each test method.

using BusinessLogic;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;

namespace TestProject.SQLite
{
    [TestClass]
    public class BlogServiceTests
    {
        [TestMethod]
        public void Add_writes_to_database()
        {
            // In-memory database only exists while the connection is open
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();

            try
            {
                var options = new DbContextOptionsBuilder<BloggingContext>()
                    .UseSqlite(connection)
                    .Options;

                // Create the schema in the database
                using (var context = new BloggingContext(options))
                {
                    context.Database.EnsureCreated();
                }

                // Run the test against one instance of the context
                using (var context = new BloggingContext(options))
                { 
                    var service = new BlogService(context);
                    service.Add("http://sample.com");
                }

                // Use a separate instance of the context to verify correct data was saved to database
                using (var context = new BloggingContext(options))
                {
                    Assert.AreEqual(1, context.Blogs.Count());
                    Assert.AreEqual("http://sample.com", context.Blogs.Single().Url);
                }
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public void Find_searches_url()
        {
            // In-memory database only exists while the connection is open
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();

            try
            {
                var options = new DbContextOptionsBuilder<BloggingContext>()
                    .UseSqlite(connection)
                    .Options;

                // Create the schema in the database
                using (var context = new BloggingContext(options))
                {
                    context.Database.EnsureCreated();
                }

                // Insert seed data into the database using one instance of the context
                using (var context = new BloggingContext(options))
                {
                    context.Blogs.Add(new Blog { Url = "http://sample.com/cats" });
                    context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" });
                    context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" });
                    context.SaveChanges();
                }

                // Use a clean instance of the context to run the test
                using (var context = new BloggingContext(options))
                {
                    var service = new BlogService(context);
                    var result = service.Find("cat");
                    Assert.AreEqual(2, result.Count());
                }
            }
            finally
            {
                connection.Close();
            }
        }
    }
}