Software: Ошибки и компромиссы при разработке ПО [Томаш Лелек] (pdf) читать постранично, страница - 106

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

кэша, так и на продолжительности пребывания элемента в кэше, а также на совокупности этих условий. При оценке новой
библиотеки стоит протестировать ожидаемое поведение, чтобы проверить свои
предположения относительно него.
Начнем эксперимент с построения простого кэша, который получает ключ
и преобразует его к верхнему регистру. В реальных системах используется более
сложное поведение загрузчика кэша, но для наших целей достаточно тривиального примера, представленного ниже.
Мы хотим проверить поведение библиотеки на основании наших предположений. В следующем листинге строится новый кэш со сроком жизни после записи, равным DEFAULT_EVICTION_TIME. CacheLoader получает значение для ключа,
предоставленного пользователем.
Листинг 9.14. Исходный вариант использования кэша
public class CacheComponent {
public static final Duration DEFAULT_EVICTION_TIME = Duration.ofSeconds(5);
public final LoadingCache cache;
public CacheComponent() {
cache =
CacheBuilder.newBuilder()
.expireAfterWrite(DEFAULT_EVICTION_TIME)
.recordStats()
.build(
new CacheLoader() {
@Override
public String load(@Nullable String key) throws Exception {
return key.toUpperCase();
}
});
}
public String get(String key) throws ExecutionException {
return cache.get(key);
}
}

Логика выглядит прямолинейно, но все равно следует проверить предположения относительно ее поведения. Код библиотеки написан не нами, поэтому он
может преподнести сюрпризы.
Требуется протестировать стратегию вытеснения используемого кэша. Для этого
нужно смоделировать задержку между вставкой элемента кэша и проверкой
процесса вытеснения. Поэтому мы должны ожидать столько, сколько идет вытеснение. В нашем сценарии использования это 5 секунд. В реальных системах

308

Глава 9. Сторонние библиотеки: используемые библиотеки становятся кодом

оно может длиться намного дольше (часы и даже дни). В следующем листинге
показан исходный, наивный подход к тестированию, требующий использования
Thread.sleep() c ожиданием DEFAULT_EVICTION_TIME.
Листинг 9.15. Тестирование без внедрения
// Дано
CacheComponent cacheComponent = new CacheComponent();
// Если
String value = cacheComponent.get("key");
// То
assertThat(value).isEqualTo("KEY");
// Если
Thread.sleep(CacheComponent.DEFAULT_EVICTION_TIME.toMillis());
// То
assertThat(cacheComponent.get("key")).isEqualTo("KEY");
assertThat(cacheComponent.cache.stats().evictionCount()).isEqualTo(1);

Обратите внимание: вытеснение производится при операции загрузки
(­get-методе). Чтобы инициировать его, необходимо вызвать метод доступа. Это
один из неожиданных аспектов, который не соотносится с предположениями
относительно библиотеки. Без качественного модульного теста это поведение,
скорее всего, обнаружить не удастся. Как уже говорилось, если время вытеснения
компонента слишком велико, тестирование компонента кэширования может
стать неприемлемым. Нужно обдумать исходный код сторонней библиотеки
(и возможно, просмотреть его), чтобы найти компонент, влияющий на поведение тестирования.
После беглого анализа выясняется, что LoadingCache при выполнении операции
чтения использует объект Ticker для определения того, должно ли значение
быть вытеснено. Доказательства приводятся в следующем листинге.
Листинг 9.16. Анализ тестируемости библиотеки кэширования
V get(K key, int hash, CacheLoader