bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] EF Core の追加処理をテストする

[C#] EF Core の追加処理をテストする

Aug 16, 2026 C# , .NET , Entity Framework Core , テスト bucket-sort

前回は、EF Core の問い合わせ処理をテストしました。

今回は、追加処理をテストします。

追加処理は一見単純です。
エンティティを作り、Add() して、SaveChangesAsync() を呼ぶだけに見えます。

しかし実務では、次のような違いが重要になります。

  • 1 件だけ追加する
  • 複数件をまとめて追加する
  • 関連データも一緒に追加する
  • 既存データと関連付けて追加する
  • 自動採番された主キーを使う
  • 保存後の状態を別の DbContext で確認する

テストでは、これらを分けて確認します。

1 件追加をテストする

まず、車を 1 件追加するメソッドです。

public async Task<int> CreateAsync(CreateCarRequest request)
{
    var car = new Car(
        request.Make,
        request.Color,
        request.PetName);

    _context.Cars.Add(car);
    await _context.SaveChangesAsync();

    return car.Id;
}

テストです。

public sealed class CarCreateTests : DatabaseTestBase
{
    [Fact]
    public async Task 車を1件追加できる()
    {
        var repository = new CarRepository(Context);

        var id = await repository.CreateAsync(new CreateCarRequest
        {
            Make = "Nissan",
            Color = "Silver",
            PetName = "Leaf"
        });

        var saved = await Context.Cars.SingleAsync(car => car.Id == id);

        Assert.Equal("Nissan", saved.Make);
        Assert.Equal("Silver", saved.Color);
        Assert.Equal("Leaf", saved.PetName);
    }
}

戻り値の ID を使って、保存されたデータを取り直しています。
追加処理では、採番された主キーが正しく返ることも重要です。

別の DbContext で確認する

同じ DbContext で確認すると、追跡中のエンティティを見ているだけになる場合があります。
保存後、本当にデータベースに入ったことを確認したいなら、別の DbContext で読み直すと安心です。

[Fact]
public async Task 追加した車を別のコンテキストから読み取れる()
{
    var repository = new CarRepository(Context);

    var id = await repository.CreateAsync(new CreateCarRequest
    {
        Make = "Mazda",
        Color = "Red",
        PetName = "Roadster"
    });

    await using var verificationContext =
        new AutoLotContext(TestDbContextOptions.Create());

    var saved = await verificationContext.Cars
        .AsNoTracking()
        .SingleAsync(car => car.Id == id);

    Assert.Equal("Mazda", saved.Make);
}

統合テストでは、同じコンテキストの追跡状態に助けられてしまうことがあります。
別コンテキストで確認すると、データベースに保存された事実をより直接確認できます。

複数件追加をテストする

複数件を追加するメソッドです。

public async Task AddRangeAsync(IEnumerable<CreateCarRequest> requests)
{
    var cars = requests.Select(request => new Car(
        request.Make,
        request.Color,
        request.PetName));

    await _context.Cars.AddRangeAsync(cars);
    await _context.SaveChangesAsync();
}

テストです。

[Fact]
public async Task 複数の車をまとめて追加できる()
{
    var before = await Context.Cars.CountAsync();
    var repository = new CarRepository(Context);

    await repository.AddRangeAsync(new[]
    {
        new CreateCarRequest
        {
            Make = "Subaru",
            Color = "Blue",
            PetName = "Impreza"
        },
        new CreateCarRequest
        {
            Make = "Suzuki",
            Color = "Yellow",
            PetName = "Swift"
        }
    });

    var after = await Context.Cars.CountAsync();

    Assert.Equal(before + 2, after);
}

件数だけでなく、必要なら追加された内容も確認します。

var names = await Context.Cars
    .Where(car => car.Make == "Subaru" || car.Make == "Suzuki")
    .Select(car => car.Make)
    .ToListAsync();

Assert.Contains("Subaru", names);
Assert.Contains("Suzuki", names);

関連データも一緒に追加する

車と注文を同時に追加するメソッドです。

public async Task<int> CreateWithOrderAsync(
    CreateCarRequest carRequest,
    string customerName)
{
    var car = new Car(
        carRequest.Make,
        carRequest.Color,
        carRequest.PetName);

    car.Orders.Add(new Order
    {
        CustomerName = customerName,
        OrderDate = DateTime.UtcNow
    });

    _context.Cars.Add(car);
    await _context.SaveChangesAsync();

    return car.Id;
}

テストです。

[Fact]
public async Task 車と注文をまとめて追加できる()
{
    var repository = new CarRepository(Context);

    var id = await repository.CreateWithOrderAsync(
        new CreateCarRequest
        {
            Make = "Daihatsu",
            Color = "Green",
            PetName = "Copen"
        },
        "山田");

    var saved = await Context.Cars
        .Include(car => car.Orders)
        .SingleAsync(car => car.Id == id);

    Assert.Single(saved.Orders);
    Assert.Equal("山田", saved.Orders[0].CustomerName);
}

EF Core は、追加されたオブジェクトグラフを見て、関連するエンティティも追加対象にします。
テストでは、親だけでなく子のデータも保存されたことを確認します。

既存データに関連付けて追加する

既存の車に注文を追加する場合を考えます。

public async Task<int> AddOrderAsync(int carId, string customerName)
{
    var order = new Order
    {
        CarId = carId,
        CustomerName = customerName,
        OrderDate = DateTime.UtcNow
    };

    _context.Orders.Add(order);
    await _context.SaveChangesAsync();

    return order.Id;
}

テストです。

[Fact]
public async Task 既存の車に注文を追加できる()
{
    var car = await Context.Cars.FirstAsync();
    var before = await Context.Orders.CountAsync(order => order.CarId == car.Id);

    var repository = new CarRepository(Context);

    await repository.AddOrderAsync(car.Id, "高橋");

    var after = await Context.Orders.CountAsync(order => order.CarId == car.Id);

    Assert.Equal(before + 1, after);
}

この方法では、車を読み込まずに外部キーだけで関連付けています。
単純な追加なら、この方が効率的です。

Attach を使った関連付けをテストする

既存エンティティを「すでにあるもの」としてコンテキストに認識させたい場合、Attach() を使います。

public async Task<int> AddOrderUsingAttachAsync(int carId, string customerName)
{
    var car = new CarReference { Id = carId };

    _context.Attach(car);

    var order = new Order
    {
        CarId = carId,
        CustomerName = customerName,
        OrderDate = DateTime.UtcNow
    };

    _context.Orders.Add(order);
    await _context.SaveChangesAsync();

    return order.Id;
}

ここでは説明のために、主キーだけを持つ参照用の考え方を示しています。
実際のエンティティのコンストラクターやプロパティ設計によっては、この書き方を調整します。

テストでは、既存の車が重複追加されないことを確認します。

[Fact]
public async Task 既存の車を重複させずに注文を追加できる()
{
    var car = await Context.Cars.FirstAsync();
    var carCountBefore = await Context.Cars.CountAsync();

    var repository = new CarRepository(Context);

    await repository.AddOrderAsync(car.Id, "伊藤");

    var carCountAfter = await Context.Cars.CountAsync();
    var orderExists = await Context.Orders.AnyAsync(order =>
        order.CarId == car.Id && order.CustomerName == "伊藤");

    Assert.Equal(carCountBefore, carCountAfter);
    Assert.True(orderExists);
}

追加処理では、関連先まで新規追加してしまう誤りが起きることがあります。
既存データとの関連付けでは、親の件数が増えていないことも確認すると安全です。

入力値の検証をテストする

エンティティやリポジトリで入力値を検証しているなら、異常系もテストします。

[Fact]
public async Task メーカー名が空なら追加できない()
{
    var repository = new CarRepository(Context);

    await Assert.ThrowsAsync<ArgumentException>(() =>
        repository.CreateAsync(new CreateCarRequest
        {
            Make = "",
            Color = "Gray",
            PetName = "NoName"
        }));
}

このテストは、データベース制約ではなく、アプリケーション側のルールを確認しています。

どの層で検証するかは設計によります。
ただ、重要なルールはテストで明示しておくと、後から変更したときに気づきやすくなります。

追加処理のテストで見ること

追加処理では、次の観点を確認します。

  • 保存後に主キーが採番される
  • 別の DbContext から読み取れる
  • 追加件数が正しい
  • 関連データも保存される
  • 既存データが重複追加されない
  • 入力値の検証が働く
  • 保存時の共通処理が動く

保存時に作成日時や更新日時を自動設定しているなら、それも確認できます。

Assert.NotEqual(default, saved.CreatedAt);
Assert.NotEqual(default, saved.UpdatedAt);

追加処理は、データの入口です。
ここをテストしておくと、後続の更新や削除のテストも組み立てやすくなります。

次回は、更新、削除、同時実行制御のテストを見ていきます。

C# .NET Entity Framework Core XUnit Add Attach 統合テスト
← [C#] EF Core の問い合わせ処理をテストする [C#] EF Core の更新と削除をテストする →

Related Posts

  • [C#] EF Core の問い合わせ処理をテストする Aug 15, 2026
  • [C#] EF Core のデータアクセス層をテストする準備 Aug 14, 2026
  • [C#] EF Core でレコードを追加する Jul 29, 2026
  • [C#] EF Core の更新と削除をテストする Aug 17, 2026

Table of Contents

  • 1 件追加をテストする
  • 別の DbContext で確認する
  • 複数件追加をテストする
  • 関連データも一緒に追加する
  • 既存データに関連付けて追加する
  • Attach を使った関連付けをテストする
  • 入力値の検証をテストする
  • 追加処理のテストで見ること

Recent Posts

  • [WPF] XAML の構文を理解する: 名前空間とキーワード Aug 23, 2026
  • [WPF] Window クラスを支える基底クラス Aug 22, 2026
  • [WPF] Window クラスの役割と上位クラス Aug 21, 2026
  • [WPF] Application クラスの役割 Aug 20, 2026
  • [WPF] WPF の主要アセンブリと名前空間 Aug 19, 2026

Categories

  • C#165
  • .NET164
  • AWS27
  • Entity Framework Core24
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • テスト4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1

Tags

  • C#
  • .NET
  • Entity Framework Core
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • LINQ
  • WPF
  • Apache
  • SQL Server
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DbContext
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • WordPress
  • アセンブリ
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • Migration
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
Powered by Hugo & Explore Theme.