0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring BootのUnitテストにおけるTransactionTemplateと@Transactionalとの使い分け

Posted at

1. はじめに

Spring Bootで統合テストを書く際、@Transactionalを使ってテスト後にデータをロールバックするのは一般的かと思います。しかし、複雑なトランザクション制御が必要なケースではTransactionTemplateが便利だったので本記事では、TransactionTemplateの実践的な使い方と、@Transactionalとの使い分けについてまとめようと思います。

2. @Transactionalによる統合テストの基本

Spring Bootでは、テストクラスやテストメソッドに@Transactionalを付けることで、テスト実行後に自動的にトランザクションがロールバックされます。

@SpringBootTest
@Transactional
class UserServiceTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testUserCreation() {
        userRepository.save(new User("Taro"));
        assertEquals(1, userRepository.count());
    }
}

この方法はシンプルで便利ですが、以下のような制約があります:

  • テストメソッド全体が1つのトランザクションになる
  • 明示的なコミットやロールバックができない
  • 非同期処理や別スレッドでのトランザクション制御が難しい

3. TransactionTemplateとは?

TransactionTemplateは、Springのトランザクション管理をプログラム的に制御できるユーティリティです。明示的にトランザクションの境界を定義できるため、テスト内で細かく制御したい場合に有効です。

@Autowired
private TransactionTemplate transactionTemplate;

@Test
void testWithTransactionTemplate() {
    transactionTemplate.execute(status -> {
        userRepository.save(new User("Hanako"));
        return null;
    });

    assertEquals(1, userRepository.count());
}

このコードでは、userRepository.save(...) の処理のみをトランザクションで囲んでいます。execute(...) メソッドの中で定義された処理ブロックが、トランザクションの境界となります。

4. 実践例:@Transactionalでは困難なケース

ケース1:テスト中に明示的なロールバックを行いたい

@Test
void testRollbackManually() {
    transactionTemplate.execute(status -> {
        userRepository.save(new User("Jiro"));
        status.setRollbackOnly(); // 明示的にロールバック
        return null;
    });

    assertEquals(0, userRepository.count());
}

ケース2:複数のトランザクションを分けてテストしたい

@Test
void testMultipleTransactions() {
    transactionTemplate.execute(status -> {
        userRepository.save(new User("A"));
        return null;
    });

    transactionTemplate.execute(status -> {
        userRepository.save(new User("B"));
        return null;
    });

    assertEquals(2, userRepository.count());
}

このテストでは、TransactionTemplate#execute() を2回呼び出すことで、2つの独立したトランザクションを順に実行しています。それぞれのexecute()ブロックは、個別のトランザクション境界を持ち、互いに影響を与えません。
@Transactionalを使った場合、テストメソッド全体が1つのトランザクションに包まれるため、途中でトランザクションを明示的に分けることはできません。このようなケースでは、TransactionTemplateのようなプログラム的トランザクション制御が有効です。

5. @Transactionalとの使い分けのポイント

シーン @Transactional TransactionTemplate
単純なロールバック付きテスト
明示的なロールバック制御
複数トランザクションの分離
非同期処理との組み合わせ
テストの簡潔さ △(やや冗長)

6. まとめ

@Transactionalはシンプルで便利ですが、複雑なトランザクション制御が必要な統合テストではTransactionTemplateが非常に有効です。使い分けることで、より堅牢で柔軟なテスト設計が可能になります。
本記事が何かのお役に立てば幸いです!

7. 参考

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?