Testando com SQLiteTesting with SQLite

SQLite tem um modo de memória que permite que você use SQLite para escrever testes em relação a um banco de dados relacional, sem a sobrecarga de operações de banco de dados real.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.

Dica

Você pode exibir este artigo exemplo no GitHubYou can view this article's sample on GitHub

Exemplo de cenário de testeExample testing scenario

Considere o seguinte serviço que permite que o código do aplicativo executar algumas operações relacionadas à blogs.Consider the following service that allows application code to perform some operations related to blogs. Ele usa internamente um DbContext que se conecta a um banco de dados do SQL Server.Internally it uses a DbContext that connects to a SQL Server database. Seria útil trocar este contexto para se conectar a um banco de dados na memória SQLite para que podemos escrever testes eficientes para esse serviço sem a necessidade de modificar o código ou muito trabalho para criar um teste duplo do contexto.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();
        }
    }
}

Prepare seu contextoGet your context ready

Evite configurar dois provedores de banco de dadosAvoid configuring two database providers

Os testes que serão externamente, configurar o contexto para usar o provedor de InMemory.In your tests you are going to externally configure the context to use the InMemory provider. Se você estiver configurando um provedor de banco de dados, substituindo OnConfiguring em seu contexto, em seguida, você precisa adicionar código condicional para garantir que você configure apenas o provedor de banco de dados se um já não tiver sido configurado.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.

Dica

Se você estiver usando o ASP.NET Core, em seguida, não será preciso esse código como o provedor de banco de dados é configurado fora do contexto (em 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");
    }
}

Adicione um construtor para testeAdd a constructor for testing

É a maneira mais simples para habilitar o teste em relação a um banco de dados diferente modificar o contexto para expor um construtor que aceite um 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)
    { }

Dica

DbContextOptions<TContext>informa o contexto de todas as suas configurações, como o banco de dados para se conectar ao.DbContextOptions<TContext> tells the context all of its settings, such as which database to connect to. Este é o mesmo objeto que é criado ao executar o método OnConfiguring em seu contexto.This is the same object that is built by running the OnConfiguring method in your context.

Testes de gravaçãoWriting tests

A chave de teste com esse provedor é a capacidade para informar o contexto para usar SQLite e controlar o escopo do banco de dados na memória.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. O escopo do banco de dados é controlado abrindo e fechando a conexão.The scope of the database is controlled by opening and closing the connection. O banco de dados é restrita a duração que a conexão está aberta.The database is scoped to the duration that the connection is open. Normalmente você deseja limpar um banco de dados para cada método de teste.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();
            }
        }
    }
}