Как проверить @Cacheable?

Я борюсь с тестированием @Cacheable в рамках Spring Boot Integration Test. Это мой второй день обучения проведению интеграционных тестов, и все найденные мной примеры используют более старые версии. Я также видел пример assetEquals("some value", is()), но ничего с оператором импорта, чтобы узнать, какая зависимость "есть". Тест не проходит на втором

Это мой интеграционный тест ....

@RunWith(SpringRunner.class)
@DataJpaTest // used for other methods
@SpringBootTest(classes = TestApplication.class)
@SqlGroup({
        @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD,
                scripts = "classpath:data/Setting.sql") })
public class SettingRepositoryIT {

    @Mock
    private SettingRepository settingRepository;

    @Autowired
    private Cache applicationCache;


    @Test
    public void testCachedMethodInvocation() {
        List<Setting> firstList = new ArrayList<>();
        Setting settingOne = new Setting();
        settingOne.setKey("first");
        settingOne.setValue("method invocation");
        firstList.add(settingOne);

        List<Setting> secondList = new ArrayList<>();
        Setting settingTwo = new Setting();
        settingTwo.setKey("second");
        settingTwo.setValue("method invocation");
        secondList.add(settingTwo);

        // Set up the mock to return *different* objects for the first and second call
        Mockito.when(settingRepository.findAllFeaturedFragrances()).thenReturn(firstList, secondList);

        // First invocation returns object returned by the method
        List<Setting> result = settingRepository.findAllFeaturedFragrances();
        assertEquals("first", result.get(0).getKey());

        // Second invocation should return cached value, *not* second (as set up above)
        List<Setting> resultTwo = settingRepository.findAllFeaturedFragrances();
        assertEquals("first", resultTwo.get(0).getKey()); // test fails here as the actual is "second."

        // Verify repository method was invoked once
        Mockito.verify(settingRepository, Mockito.times(1)).findAllFeaturedFragrances();
        assertNotNull(applicationCache.get("findAllFeaturedFragrances"));

        // Third invocation with different key is triggers the second invocation of the repo method
        List<Setting> resultThree = settingRepository.findAllFeaturedFragrances();
        assertEquals(resultThree.get(0).getKey(), "second");
    }
}

ApplicationContext, компоненты, сущности, репозитории и уровень обслуживания для тестов. Причина, по которой я делаю это таким образом, заключается в том, что этот модуль maven используется в других модулях в качестве зависимости.

@ComponentScan({ "com.persistence_common.config", "com.persistence_common.services" })
@EntityScan(basePackages = { "com.persistence_common.entities" })
@EnableJpaRepositories(basePackages = { "com.persistence_common.repositories" })
@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Конфигурация кеша ....

@Configuration
@EnableCaching
public class CacheConfig {

    public static final String APPLICATION_CACHE = "applicationCache";

    @Bean
    public FilterRegistrationBean registerOpenSessionInViewFilterBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
        registrationBean.setFilter(filter);
        registrationBean.setOrder(5);
        return registrationBean;
    }


    @Bean
    public Cache applicationCache() {
        return new GuavaCache(APPLICATION_CACHE, CacheBuilder.newBuilder()
                .expireAfterWrite(30, TimeUnit.DAYS)
                .build());
    }
}

Тестируемый репозиторий ....

public interface SettingRepository extends JpaRepository<Setting, Integer> {

    @Query(nativeQuery = true, value = "SELECT * FROM Setting WHERE name = 'featured_fragrance'")
    @Cacheable(value = CacheConfig.APPLICATION_CACHE, key = "#root.methodName")
    List<Setting> findAllFeaturedFragrances();
}

person Grim    schedule 13.05.2017    source источник
comment
is(...), скорее всего, будет иметь отношение к is Hamcrest CoreMatcher. Hamcrest часто используется в сочетании с JUnits assertThat(actual, matcher), поскольку он обеспечивает более свободный стиль чтения утверждений. Я также задал вопрос относительно кеша Spring некоторое время назад, где я использовал модульный тест, чтобы показать мою проблему. Может быть, это в чем-то поможет   -  person Roman Vottner    schedule 17.05.2017
comment
хорошо спасибо Роман ...   -  person Grim    schedule 19.05.2017


Ответы (2)


Первая проблема с SettingRepositoryIT - это пометка @Mock в поле settingRepository. Это парадокс для любого нормального теста, интеграционного теста или любого другого.

Вы должны позволить Spring внести зависимости для тестируемого класса, которым в вашем случае является SettingRepository.

Посмотрите в этом примере, как @Autowired используется для тестируемого класса, которым в данном примере является OrderService:

@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from the
// static nested Config class
@ContextConfiguration
public class OrderServiceTest {

    @Configuration
    static class Config {

        // this bean will be injected into the OrderServiceTest class
        @Bean
        public OrderService orderService() {
            OrderService orderService = new OrderServiceImpl();
            // set properties, etc.
            return orderService;
        }
    }

    @Autowired
    private OrderService orderService;

    @Test
    public void testOrderService() {
        // test the orderService
    }

}

Обратитесь к документации с полным примером: § 15. Интеграционное тестирование

Вторая проблема заключается в том, что вам не нужно тестировать @Cachable. Вам следует только протестировать свою реализацию. Вот очень хороший пример от Оливера Гирке о том, как вы должны его протестировать: Как протестировать поддержку декларативного кеширования Spring в репозиториях Spring Data?

person Tobias Otto    schedule 22.05.2017
comment
Неправильно, что вам не нужно тестировать Cachable, чтобы чувствовать себя в безопасности, у меня должны быть инструменты для тестирования Cachable. Если нет, как я могу быть уверен, что все мои настройки кэша JPA верны? Все необходимые аннотации есть? Все компоненты кеша находятся в контейнере во время выполнения? Я не могу доверять этому без тестирования. В любом случае я буду тестировать Cachable с помощью Spring Test или без него, сделав тестовый пример. Мне кажется, что отсутствие возможности протестировать его с помощью SpringTest является недостатком. - person Andreas Gelever; 07.07.2018

В моем случае я хотел проверить выражение в выражении if в аннотации @Cacheable, поэтому я думаю, что это имеет смысл, и я не тестирую код Spring.

Мне удалось протестировать его без использования Spring Boot, так что это простой тест Spring:

@RunWith(SpringRunner.class)
@ContextConfiguration
public class MyTest {

    @Configuration
    @EnableCaching
    static class Config {

        @Bean
        public MyCacheableInterface myCacheableInterface() {
            return (authorization) -> createTestResult();
        }

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

    @Autowired
    private MyCacheableInterface myCacheableInterface;

В MyCacheableInterface у меня есть следующая аннотация:

public interface MyCacheableInterface {
    @Cacheable(value = "myObject", unless = "#result.?[Retorno.getSucesso() != 'S'].size() == #result.size()")
    List<MyObject> businessMethod(String authorization);
}
person Constantino Cronemberger    schedule 23.05.2019