Статьи

Реализация тестовых коллекций с использованием Guava

В настоящее время я добавляю новую функцию в LibFX , для которой я создаю некоторые пользовательские коллекции, схожие с таковыми из Java Collections Framework . Я отправился на поиски тестов, которые мог бы выполнить против них, и был рад узнать, что Google Guava содержит именно то, что мне нужно: массивный набор тестов, который проверяет каждый уголок моей реализации для всех интерфейсов сбора из JDK и Guava.

Давайте кратко рассмотрим это.

обзор

В этом посте сначала будет показано, как настроить проект, а затем посмотреть, как начать работу с реальными тестами.

Я не создал специальных примеров, но вы можете видеть, как я использовал это в LibFX .

Настроить

Чтобы это работало, нам нужен JUnit, Guava-Testlib и небольшой шаблонный код.

Получить JUnit

Если вы еще не используете JUnit в своем проекте, загрузите его здесь . Если вы используете Maven или Gradle:

Информация о зависимостях для Maven

1
2
3
4
5
6
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

Информация о зависимостях для Gradle

1
testCompile 'junit:junit:4.12'

Получить гуаву

Что нам действительно нужно, так это не сама Гуава, а Гуава-Тестлиб . Вы можете скачать его из центрального репозитория , который также содержит информацию о зависимостях для разных менеджеров.

Для вашего удобства:

Информация о зависимостях для Maven

1
2
3
4
5
6
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava-testlib</artifactId>
    <version>18.0</version>
    <scope>test</scope>
</dependency>

Информация о зависимостях для Gradle

1
testCompile 'com.google.guava:guava-testlib:18.0'

Напишите немного

Предположим, вы хотите написать MySet и соответствующий MySetTest .

Делая это JUnit-3.8.x-way, создайте метод public static Test suite();. JUnit ищет этот метод и использует его для определения всех тестов, которые он будет выполнять для этого класса. Внутри этого метода создайте TestSuite и добавьте тесты, которые мы собираемся записать ниже:

Котел в «MySetTest»

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class MySetTest {
 
    public static Test suite() {
        return new MySetTest().allTests();
    }
 
    public Test allTests() {
        TestSuite suite =
            new TestSuite("package.name.of.MySetTest");
        suite.addTest(testForOneToWayUseMySet());
        suite.addTest(testForAnotherWayToUseMySet());
        return suite;
    }
     
}

(Я не пытался сделать это с помощью аннотаций JUnit 4. Если вы это сделали, пингуйте меня, и я включу это здесь.)

Имея этот шаблон, вы можете запускать этот класс с JUnit, например, изнутри вашей IDE или с вашего CI-сервера.

Проверьте свои реализации

Теперь, когда это сделано, мы можем начать создавать тесты для наших реализаций. Или, точнее, скажите Гуаве, как это сделать для нас. Это процесс, состоящий из двух частей: один создает генератор для элементов в коллекции и тестируемый модуль, другой использует один из сборщиков тестовых наборов Guava для создания полного набора тестов, адаптированных к реализации.

Мы продолжим тестировать реализацию Set . Ниже мы увидим, для каких других интерфейсов доступны тестовые наборы.

Генератор для элементов и тестируемого устройства

Разработчик набора тестов требует, чтобы вы дали ему возможность создавать образцы элементов в коллекции и создавать экземпляры вашей коллекции. Для этого вам нужно реализовать TestSetGenerator<E> (где E — тип элементов).

Это прямо с order(List<E>) который является единственным методом, который может потребовать некоторого размышления. Обратите внимание, что в отличие от документации, текущая версия testlib (18.0) вызывает этот метод, даже если CollectionFeature.KNOWN_ORDER не сообщается (подробности о функциях см. Ниже). В моем случае достаточно вернуть порядок вставки.

Test Suite Builder

Теперь, когда происходит настоящее волшебство. Вы берете свой генератор сверху, передаете его правильному сборщику тестов, указываете, какие функции имеет ваша коллекция, и он создаст индивидуальный и всеобъемлющий набор тестов:

Использование SetTestSuiteBuilder для создания тестов

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public Test testForOneToWayUseMySet() {
    return SetTestSuiteBuilder
            .using(new MySetGenerator())
            .named("one way to use MySet")
            .withFeatures(
                    CollectionSize.ANY,
                    CollectionFeature.ALLOWS_NULL_VALUES,
                    CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
                    CollectionFeature.SUPPORTS_ADD,
                    CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
                    CollectionFeature.SUPPORTS_REMOVE,
            )
            .createTestSuite();
}

Особенности

Важно указать правильные функции. Взгляните на два перечисления CollectionSize и CollectionFeatures чтобы увидеть, какие существуют возможности для описания поведения вашей коллекции.

Обратите внимание, что созданные тесты проверяют возможности в обоих направлениях! Например, если не ALLOWS_NULL_VALUES сгенерирует тесты, которые проверят, что добавление null в коллекцию приводит к NullPointerException .

Подавляющие тесты

Вызывая suppressing на компоновщике, вы можете указать методы тестирования, которые не будут выполняться. Похоже, что существует последнее средство, когда функции не позволяют точно определить поведение. Я не использовал это.

Установка и удаление

Если вам нужно выполнить код до или после каждого теста, вы можете передать его как Runnable для withSetUp или withTearDown , соответственно (оба могут быть вызваны в компоновщике).

Доступные тесты

Конечно, вы можете создавать тестовые наборы и для других интерфейсов. Первый взгляд дает следующие возможности:

Коллекции Java:

  • Коллекция
  • Итератор
  • Список
  • карта
  • NavigableMap
  • NavigableSet
  • Очередь
  • Поставил
  • SortedMap
  • SortedSet

Коллекции Гуавы:

  • BiMap
  • ListMultimap
  • Multimap
  • Multiset
  • SetMultimap
  • SortedMultiset
  • SortedSetMultimap

Поиск типа для * TestSuiteBuilder (обратите внимание на подстановочный знак) приводит к появлению некоторых других сборщиков. Я не исследовал их, но возможно, что они могут быть использованы для создания тестов для других случаев.

Чтобы использовать их, просто внедрите соответствующий Test...Generator и передайте его соответствующему ...TestSuiteBuilder .

отражение

Мы увидели, как тестировать реализации коллекций с помощью Testlib из Guava: как включить его и JUnit в наш проект, какой шаблон нам нужен для его запуска, и обзор генератора и генератора тестовых наборов. В последнем случае происходит вся магия, поскольку она создает всесторонние тесты с учетом нашего описания нашей реализации и ее возможностей.

Ссылка: Реализация тестовой коллекции с Guava от нашего партнера JCG Николая Парлога в блоге CodeFx .