Test de SQLiteTesting with SQLite

SQLite a un mode en mémoire qui vous permet d’utiliser SQLite pour écrire des tests par rapport à une base de données relationnelle, sans la surcharge des opérations de base de données.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.

Conseil

Vous pouvez afficher cet article exemple sur GitHubYou can view this article's sample on GitHub

Exemple de scénario de testExample testing scenario

Envisagez le service suivant qui permet au code d’application effectuer certaines opérations liées à des blogs.Consider the following service that allows application code to perform some operations related to blogs. Il utilise en interne un DbContext qui se connecte à une base de données SQL Server.Internally it uses a DbContext that connects to a SQL Server database. Il peut être utile d’échange de ce contexte pour se connecter à une base de données en mémoire SQLite afin que nous pouvons écrire des tests efficaces pour ce service sans avoir à modifier le code ou effectuer beaucoup de travail pour créer un test double du contexte.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();
        }
    }
}

Préparer votre contexteGet your context ready

Évitez de configurer les deux fournisseurs de base de donnéesAvoid configuring two database providers

Dans vos tests, vous vous apprêtez à l’extérieur de configurer le contexte pour utiliser le fournisseur en mémoire.In your tests you are going to externally configure the context to use the InMemory provider. Si vous configurez un fournisseur de base de données en remplaçant OnConfiguring dans votre contexte, vous devez ensuite ajouter du code conditionnel afin de configurer le fournisseur de base de données uniquement si une n’a pas déjà été configurée.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.

Conseil

Si vous utilisez ASP.NET Core, puis vous devez pas ce code depuis votre fournisseur de base de données est configurée en dehors du contexte (dans Startup.cs).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");
    }
}

Ajoutez un constructeur pour le testAdd a constructor for testing

La méthode la plus simple pour activer le test par rapport à une autre base de données consiste à modifier votre contexte pour exposer un constructeur qui accepte un 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)
    { }

Conseil

DbContextOptions<TContext>Indique le contexte de tous ses paramètres, tels que la base de données pour se connecter à.DbContextOptions<TContext> tells the context all of its settings, such as which database to connect to. Il s’agit de l’objet qui est généré par la méthode OnConfiguring en cours d’exécution dans votre contexte.This is the same object that is built by running the OnConfiguring method in your context.

Écriture de testsWriting tests

La clé à tester avec ce fournisseur est la possibilité d’indiquer le contexte à utiliser SQLite, contrôler l’étendue de la base de données en mémoire.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. L’étendue de la base de données est contrôlé par l’ouverture et de fermeture de la connexion.The scope of the database is controlled by opening and closing the connection. La portée de la base de données est limitée à la durée pendant laquelle la connexion est ouverte.The database is scoped to the duration that the connection is open. En règle générale, vous souhaitez une nouvelle base de données pour chaque méthode de test.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();
            }
        }
    }
}