業務アプリの品質を高めるには、 DBを含めたテスト(Integration Test)が欠かせません。 しかし、実ファイルのSQLiteを使うと「遅い・壊れる・後片付けが面倒」という問題が出ます。
そこで最強なのが、SQLiteのInMemoryモード。 メモリ上にDBを作るため、 高速・副作用ゼロ・テストごとに初期化可能というメリットがあります。
この記事でわかること
・SQLite InMemoryの基本
・テストDBの初期化方法
・Repository + Unit of Workとの組み合わせ
・Dapper / EF Core 両対応の実装例
・業務アプリ向けテスト戦略
・SQLite InMemoryの基本
・テストDBの初期化方法
・Repository + Unit of Workとの組み合わせ
・Dapper / EF Core 両対応の実装例
・業務アプリ向けテスト戦略
1. SQLite InMemoryとは?
SQLiteには、ファイルを作らずメモリ上にDBを作るモードがあります。
■ 接続文字列
Data Source=:memory:;Cache=Shared
Cache=Shared が重要で、 複数の接続から同じInMemory DBにアクセスできます。
2. InMemory DBの特徴
- 超高速(ファイルI/Oなし)
- テスト終了と同時に消える(副作用ゼロ)
- 毎回初期化できる(再現性が高い)
- Repository/UoWと相性抜群
3. InMemory DBの作成(C#)
using Microsoft.Data.Sqlite;
var cs = "Data Source=:memory:;Cache=Shared";
var con = new SqliteConnection(cs);
con.Open();
// テーブル作成
var cmd = con.CreateCommand();
cmd.CommandText = @"
CREATE TABLE Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Age INTEGER
);";
cmd.ExecuteNonQuery();
ポイントは接続を閉じないこと。 InMemory DBは「最後の接続が閉じられた瞬間に消える」ためです。
4. テスト用DBを初期化する関数
public static SqliteConnection CreateInMemoryDb()
{
var cs = "Data Source=:memory:;Cache=Shared";
var con = new SqliteConnection(cs);
con.Open();
using var cmd = con.CreateCommand();
cmd.CommandText = @"
CREATE TABLE Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Age INTEGER
);
";
cmd.ExecuteNonQuery();
return con; // 接続を保持したまま返す
}
5. Repository + Unit of Work と組み合わせる
InMemory DBは、Repository/UoWと組み合わせると最強になります。
■ テスト用Unit of Work
public class TestUnitOfWork : IUnitOfWork
{
private readonly SqliteConnection _con;
private readonly SqliteTransaction _tran;
public IUserRepository Users { get; }
public TestUnitOfWork(SqliteConnection con)
{
_con = con;
_tran = _con.BeginTransaction();
Users = new SqliteUserRepository(_con, _tran);
}
public Task CommitAsync() => _tran.CommitAsync();
public Task RollbackAsync() => _tran.RollbackAsync();
public ValueTask DisposeAsync()
{
_tran.Dispose();
return ValueTask.CompletedTask;
}
}
6. テストコード例(xUnit)
public class UserRepositoryTests
{
[Fact]
public async Task AddUser_ShouldInsertCorrectly()
{
var con = TestDb.CreateInMemoryDb();
await using var uow = new TestUnitOfWork(con);
var user = new User { Name = "Taro", Age = 20 };
await uow.Users.AddAsync(user);
await uow.CommitAsync();
var result = await uow.Users.GetByIdAsync(1);
Assert.Equal("Taro", result.Name);
}
}
テストごとにDBが初期化されるため、 テストの独立性が高く、再現性も完璧です。
7. EF CoreでInMemory SQLiteを使う場合
EF Coreには「InMemory Provider」がありますが、 SQLiteの動作と異なるため非推奨です。
代わりに、UseSqlite + InMemory接続を使うのが正解。
var con = new SqliteConnection("Data Source=:memory:");
con.Open();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(con)
.Options;
using var db = new AppDbContext(options);
db.Database.EnsureCreated();
8. テストデータ投入(Seed)
テスト開始時にデータを投入する関数を用意しておくと便利。
public static void SeedUsers(SqliteConnection con)
{
var sql = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";
con.Execute(sql, new { name = "Taro", age = 20 });
con.Execute(sql, new { name = "Hanako", age = 25 });
}
9. 業務アプリ向けベストプラクティス
- InMemory DBはテスト専用にする
- 接続はテスト中ずっと保持する(閉じるとDBが消える)
- Repository + UoW と組み合わせると最強
- EF CoreのInMemory Providerは使わない(SQLiteと挙動が違う)
- Seedデータでテストの再現性を高める
- テストは「1テスト=1DB初期化」が基本
まとめ:InMemory SQLiteは“高速・安全・再現性100%”の最強テスト環境
- ファイルを作らないため超高速
- テスト終了と同時に消える(副作用ゼロ)
- 毎回初期化できるため再現性が高い
- Repository + UoW と相性抜群
「実DBに近い動作で高速にテストしたい」 そんなニーズに最も応えるのが InMemory SQLite です。 この記事をベースに、あなたのアプリに最適なテスト基盤を構築してみてください。