1. 概要

実装に加えて、 Springの宣言型キャッシュメカニズムを使用して、インターフェイスに注釈を付けることができます。 たとえば、SpringDataリポジトリでキャッシュを宣言できます。

このチュートリアルでは、このようなシナリオをテストする方法を示します。

2. 入門

まず、簡単なモデルを作成しましょう。

@Entity
public class Book {

    @Id
    private UUID id;
    private String title;

}

次に、@Cacheableメソッドを持つリポジトリインターフェイスを追加しましょう。

public interface BookRepository extends CrudRepository<Book, UUID> {

    @Cacheable(value = "books", unless = "#a0=='Foundation'")
    Optional<Book> findFirstByTitle(String title);

}

ここでの条件が必須ではない場合を除き、。 これは、キャッシュミスのシナリオをすぐにテストするのに役立ちます。

また、読みやすい「#title」ではなく、SpEL式「#a0」に注意してください。 これを行うのは、プロキシがパラメータ名を保持しないためです。 したがって、代替の#root.arg [0]、p0、またはa0表記を使用します。

3. テスト

私たちのテストの目標はキャッシュメカニズムが機能することを確認します。 したがって、私たちは Spring Dataリポジトリの実装をカバーするつもりはありませんまたは永続性の側面。

3.1. スプリングブーツ

簡単なSpring Bootテストから始めましょう。

まず、テストの依存関係を設定し、いくつかのテストデータを追加し、本がキャッシュにあるかどうかを確認するための簡単なユーティリティメソッドを作成します。

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = CacheApplication.class)
public class BookRepositoryIntegrationTest {

    @Autowired
    CacheManager cacheManager;

    @Autowired
    BookRepository repository;

    @BeforeEach
    void setUp() {
        repository.save(new Book(UUID.randomUUID(), "Dune"));
        repository.save(new Book(UUID.randomUUID(), "Foundation"));
    }

    private Optional<Book> getCachedBook(String title) {
        return ofNullable(cacheManager.getCache("books")).map(c -> c.get(title, Book.class));
    }

それでは、本をリクエストした後、それがキャッシュに配置されることを確認しましょう。

    @Test
    void givenBookThatShouldBeCached_whenFindByTitle_thenResultShouldBePutInCache() {
        Optional<Book> dune = repository.findFirstByTitle("Dune");

        assertEquals(dune, getCachedBook("Dune"));
    }

また、一部の書籍がキャッシュに配置されていない

    @Test
    void givenBookThatShouldNotBeCached_whenFindByTitle_thenResultShouldNotBePutInCache() {
        repository.findFirstByTitle("Foundation");

        assertEquals(empty(), getCachedBook("Foundation"));
    }

このテストでは、Springが提供する CacheManager を利用し、をチェックして、各repository.findFirstByTitle操作の後に、CacheManagerにに従って本が含まれる(または含まれない)ことを確認します。 ]@Cacheableルール。

3.2. プレーンスプリング

Spring統合テストを続けましょう。 そして変更のために、今回は私たちのインターフェースをモックしましょう。 次に、さまざまなテストケースでの相互作用を検証します。

まず、BookRepositorymock実装を提供する@Configurationを作成します。

@ContextConfiguration
@ExtendWith(SpringExtension.class)
public class BookRepositoryCachingIntegrationTest {

    private static final Book DUNE = new Book(UUID.randomUUID(), "Dune");
    private static final Book FOUNDATION = new Book(UUID.randomUUID(), "Foundation");

    private BookRepository mock;

    @Autowired
    private BookRepository bookRepository;

    @EnableCaching
    @Configuration
    public static class CachingTestConfig {

        @Bean
        public BookRepository bookRepositoryMockImplementation() {
            return mock(BookRepository.class);
        }

        @Bean
        public CacheManager cacheManager() {
            return new ConcurrentMapCacheManager("books");
        }

    }

モックの動作の設定に進む前に、このコンテキストでMockitoを正常に使用することについて言及する価値のある2つの側面があります。

  • BookRepository モックのプロキシです。したがって、 Mockito 検証を使用するために、AopTestUtils.getTargetObjectを介して実際のモックを取得します。
  • CachingTestConfig は1回しか読み込まれないため、テストの合間には必ずreset(mock)を実行してください。
    @BeforeEach
    void setUp() {
        mock = AopTestUtils.getTargetObject(bookRepository);

        reset(mock);

        when(mock.findFirstByTitle(eq("Foundation")))
                .thenReturn(of(FOUNDATION));

        when(mock.findFirstByTitle(eq("Dune")))
                .thenReturn(of(DUNE))
                .thenThrow(new RuntimeException("Book should be cached!"));
    }

これで、テストメソッドを追加できます。 まず、書籍がキャッシュに配置された後、後でその書籍を取得しようとしたときに、リポジトリの実装とのやり取りがないことを確認します。

    @Test
    void givenCachedBook_whenFindByTitle_thenRepositoryShouldNotBeHit() {
        assertEquals(of(DUNE), bookRepository.findFirstByTitle("Dune"));
        verify(mock).findFirstByTitle("Dune");

        assertEquals(of(DUNE), bookRepository.findFirstByTitle("Dune"));
        assertEquals(of(DUNE), bookRepository.findFirstByTitle("Dune"));

        verifyNoMoreInteractions(mock);
    }

また、キャッシュされていない本の場合、毎回リポジトリを呼び出すことを確認したいと思います。

    @Test
    void givenNotCachedBook_whenFindByTitle_thenRepositoryShouldBeHit() {
        assertEquals(of(FOUNDATION), bookRepository.findFirstByTitle("Foundation"));
        assertEquals(of(FOUNDATION), bookRepository.findFirstByTitle("Foundation"));
        assertEquals(of(FOUNDATION), bookRepository.findFirstByTitle("Foundation"));

        verify(mock, times(3)).findFirstByTitle("Foundation");
    }

4. 概要

要約すると、Spring、Mockito、およびSpring Bootを使用して、インターフェイスに適用されたキャッシュメカニズムが正しく機能することを確認する一連の統合テストを実装しました。

上記のアプローチを組み合わせることもできることに注意してください。 たとえば、Spring Bootでモックを使用したり、プレーンなSpringテストでCacheManagerのチェックを実行したりすることを妨げるものは何もありません。

完全なコードは、GitHubから入手できます。