SQLite でテストします。Testing with SQLite

SQLite は、SQLite を使用して実際のデータベース操作のオーバーヘッドがなく、リレーショナル データベースに対してテストを記述できるようにする、インメモリ モードです。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.

ヒント

この記事を表示するサンプルgithubYou can view this article's sample on GitHub

テスト シナリオの例Example testing scenario

ブログに関連するいくつかの操作を実行するアプリケーション コードは、次のサービスを検討してください。Consider the following service that allows application code to perform some operations related to blogs. 内部的に使用して、 DbContext SQL Server データベースに接続します。Internally it uses a DbContext that connects to a SQL Server database. コードを変更しなくてもこのサービスの効率的なテストを記述またはテストを作成する作業の多くを実行できるようにする、インメモリ SQLite データベースに接続するには、このコンテキストを交換すると便利だコンテキストの二重です。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();
        }
    }
}

コンテキストを整えるGet your context ready

2 つのデータベース プロバイダーを構成しません。Avoid configuring two database providers

テストにしようとする InMemory プロバイダーを使用するコンテキストの外部で構成します。In your tests you are going to externally configure the context to use the InMemory provider. オーバーライドすることで、データベース プロバイダーを構成する場合OnConfiguringコンテキストにする必要がいずれかが既に構成されていない場合のみ、データベース プロバイダーを構成することを確認する条件付きコードを追加します。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.

ヒント

ASP.NET Core を使用している場合、必要はありませんこのコード (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");
    }
}

テストするためのコンス トラクターを追加します。Add a constructor for testing

別のデータベースに対してテストを有効にする最も簡単な方法は、コンテキストを受け取るコンス トラクターを公開するを変更する、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)
    { }

ヒント

DbContextOptions<TContext>すべてに接続するデータベースなど、その設定のコンテキストに指示します。DbContextOptions<TContext> tells the context all of its settings, such as which database to connect to. これは、同じオブジェクトのコンテキストで OnConfiguring メソッドを実行して組み込まれています。This is the same object that is built by running the OnConfiguring method in your context.

テストの記述Writing tests

このプロバイダーでのテストに、キーは、SQLite を使用して、メモリ内のデータベースのスコープを制御するコンテキストを確認する機能です。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. データベースのスコープは、接続の開閉によって制御されます。The scope of the database is controlled by opening and closing the connection. データベースは、接続が開いている間に制限されます。The database is scoped to the duration that the connection is open. 通常、クリーンなデータベースは各テスト メソッドにします。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();
            }
        }
    }
}