Для заданий Hadoop MapReduce существует специальная архитектура кода, которая следует определенному шаблону с определенными конструкциями. При разработке через тестирование (TDD) и написании модульных тестов эта архитектура создает интересные проблемы. Используя MRUnit, Mockito и PowerMock, это реальный пример. я собираюсь коснуться
1) Используйте MRUnit для приложений Hadoop MR для написания проверок JUnit.
2) Издеваться над статическими методами с помощью PowerMock и Mockito.
3) Моделирование бизнес-логики, найденной в другом классе.
4) Проверка смоделированной бизнес-логики.
5) счетчики тестирования,
6) операторы тестирования условного блока log4j.
7) Обработка исключений тестирования. Я уверен, что читатель уже знаком с JUnit 4.
Вы можете создать тестовые входные данные с помощью MRUnit, протолкнуть их через свой преобразователь и/или редуктор и проверить, все ли они выводятся в тесте JUnit. Это поможет вам отлаживать код, используя тест JUnit в качестве механизма, как это делают другие тесты JUnit. Пара map/reduce может быть оценена с помощью MapReduceDriver из MRUnit. Комбинатор также можно оценить с помощью MapReduceDriver. PipelineMapReduceDriver помогает протестировать рабочий процесс сопоставления/сокращения заданий. В настоящее время разделители под MRUnit не имеют тест-драйва. MRUnit поможет вам выполнить TDD и написать легкие модульные тесты, которые учитывают сложную архитектуру и конструкции Hadoop. Дополнительную информацию см. в курсе Hadoop для больших данных.
Пример в MRUnit
Мы обрабатываем данные дорожного покрытия, используемые для построения карт в следующем примере. Линейные поверхности (описывающие участок дороги) и пересечения (описывающие пересечение дорог) включены во входные данные. Набор этих смешанных поверхностей берется в качестве входных данных этим картографом и отбрасывается все, что не является линейной дорожной поверхностью. Затем обрабатывает и записывает в HDFS каждое дорожное покрытие. Мы хотим отслеживать количество добавленных внедорожных поверхностей и, наконец, распечатывать их. Кроме того, мы распечатаем, сколько дорожных покрытий было обработано в целях отладки.
открытый класс MergeAndSplineMapper расширяет Mapper‹LongWritable, BytesWritable, LongWritable, BytesWritable› {
частный статический Logger LOG = Logger.getLogger(MergeAndSplineMapper.class);
перечисление SurfaceCounters {
ДОРОГИ, НЕЛИНЕЙНЫЕ, НЕИЗВЕСТНЫЕ
}
@Override
карта public void (ключ LongWritable, значение BytesWritable, контекст контекста) выдает IOException, InterruptedException {
LinkSurfaceMap lsm = (LinkSurfaceMap) BytesConverter.bytesToObject(value.getBytes());
List‹RoadSurface›mixedSurfaces = lsm.toSurfaceList();
для (поверхность RoadSurface: смешанные поверхности) {
Long surfaceId = surface.getNumericId();
Enums.SurfaceType surfaceType = surface.getSurfaceType();
если ( SurfaceType.equals(SurfaceType.INTERSECTION) ) {
context.getCounter(SurfaceCounters.NONLINEARS).increment(1);
Продолжать;
}
иначе если ( ! SurfaceType.equals(SurfaceType.ROAD)) {
context.getCounter(SurfaceCounters.UNKNOWN).increment(1);
Продолжать;
}
PopulatorPreprocessor.processLinearSurface(поверхность);
лсм.setSurface (поверхность);
context.write(новый LongWritable(surfaceId), новый BytesWritable(BytesConverter.objectToBytes(lsm)));
если (LOG.isDebugEnabled()) {
context.getCounter(SurfaceCounters.ROADS).increment(1);
}
}
}
}
Разборка MRUnit
Если вы посмотрите на наш тестовый класс, мы просто проверяем идентификатор поверхности и форму поверхности, отбрасываем все, что не является дорожным покрытием, увеличиваем эти счетчики и обрабатываем дорожные покрытия. Давайте посмотрим на testMap INTERSECTION), (первый тест.
Тестовая карта INTERSECTION
Наша цель — подтвердить
- Поверхностные счетчики.
- С НЕЛИНЕЙНЫМ.
- PopulatorPreprocessor.processLinearSurface(surface) никогда не переименовывается, т. е. цикл for продолжается.
Нет приращений.
Поскольку это преобразователь, мы начнем с определения и инициализации драйвера для преобразователя. Обратите внимание, что четыре параметра типа, указанные для MapDriver, то есть MergeAndSplineMapper, должны соответствовать нашему тестовому классу.
LongWritable, BytesWritable, LongWritable, BytesWritable › mapDriver; частный MapDriver;
От @Before
Настройка) ({public void setUp)
MergeAndSplineMapper mapper = новая версия MergeAndSplineMapper);
Новый MapDriver ‹ LongWritable, BytesWritable, LongWritable, BytesWritable ›);; (mapDriver=
Картограф (картограф) mapDriver.setMapper;
}
Вызов исключения IOException для подписи системы модульного тестирования в MRUnit
Картограф смог создать исключение IOException. Вы можете обрабатывать исключения, создаваемые кодом вызывающего объекта в тестах JUnit, перехватывая или выбрасывая их. Имейте в виду, что мы не тестируем исключения явно. Я предпочитаю не захватывать исключение и отбрасывать его в процессе модульного тестирования. Тест завершится ошибкой, если метод модульного тестирования обнаружит исключение. Что будет то, что мы хотим. Когда вы явно не проверяете обработку исключений, попытка перехватить исключения в модульных тестах может привести к ненужному беспорядку, рассуждениям, обслуживанию, когда вы можете сгенерировать исключение, чтобы провалить тест.
Тест
IOException {выдает public void testMap INTERSECTION)
Чтобы запустить тест, инициализируйте тестовый ввод. Мы должны гарантировать, что форма поверхности будет RoadType, чтобы достичь if-блока, который мы хотим протестировать.
ПЕРЕСЕЧЕНИЕ
LinkSurfaceMap lsm = недавно разработанная LinkSurfaceMap);
RoadSurface rs = new(Enums. RoadType. INTERSECTION) RoadSurface;
Байт] [lsmBytes = append(lsm, rs); (лсм, рс);
Для имитации статического вызова класса PopulatorPreprocessor мы используем PowerMock[3]. Отдельный класс, содержащий бизнес-логику, называется PopulatorPreprocessor и оценивается его тестом JUnit. Мы настраиваем PowerMock на уровне класса с помощью
С помощью @RunWith
Аннотировать и указать, какие классы имитировать; PopulatorPreprocessor, в данном случае один. с с
Проверка @PrepareForTest в MRUnit
Мы сообщаем PowerMock, какие классы, которые мы хотим имитировать, имеют статические методы. PowerMock поддерживает как EasyMock, так и Mockito, поэтому вы увидите ссылки на PowerMockito, потому что мы используем Mockito. Назвав PowerMockito.mockStatic, мы высмеиваем статический класс.
(PowerMockRunner.класс) @RunWith
(PopulatorPreprocessor.class) @PrepareForTest
PowerMockito.mockStatic(Preprocessor.classPopulator);
Установите тестовый ввод, сгенерированный ранее, и запустите картограф:
Текущие доступные для записи байты (lsmBytes); mapDriver.withInput (новый LongWritable (1234567));
(); mapDriver.runTest();
Проверьте производительность. Поверхностные счетчики.
С НЕЛИНЕЙНЫМИ
Один раз, и SurfaceCounters увеличиваются.
ПОЛОСЫ
SurfaceCounters и.
НЕИЗВЕСТНО
Прибавок нет. Быстрый обзор — сообщение об ошибке утверждения — это первый параметр, необязательная строка с assertEquals JUnit. Расчетное значение является вторым параметром, а реальное значение является третьим параметром. AssertEquals выводит красивое сообщение об ошибке «ожидалось: ‹ x ›, но было: ‹ y ›», поэтому, если, например, нужно было выполнить второе утверждение, мы получили бы сообщение об ошибке «java.lang. AssertionError: неверный подсчет NONLINEARS». По оценкам:‹1 ›, но было:‹0 ›.
Assert.assertEquals("ДОРОГИ считаются ошибочными.," 0,0, 0 , 0 , 0, assertEquals
MapDriver.getCounters().roads.getValue()).findCounter(SurfaceCounters. ДОРОГИ);;
Assert.assertEquals("Неверный счет НЕЛИНЕЙНЫХ СЧЕТОВ., "1, 1 , 1", Неверный счет НЕЛИНЕЙНЫХ СЧЕТОВ.
MapDriver.getCounters().findCounter(NONLINEARS.SurfaceCounters).getValue));;);
Assert.assertEquals("Неверный неизвестный счетчик., "0 , 0", Неверный неизвестный счетчик
MapDriver.getCounters().findCounter(.UNKNOWN).getValue));;;);
Убедитесь, что с помощью следующего синтаксиса PowerMock/Mockito PopulatorPreprocessor.processLinearSurface(surface) не был переименован.
PowerMockito.verifyStatic(Mockito.never));;);
PreprocessorPopulator.processLinearSurface(rs);
Тестовый маршрутКарта ROAD
Тестовая карта ДОРОГА). (в нашем втором тесте. Наша цель — проверка:
Поверхностные счетчики.
ПОЛОСЫ
Увеличение есть.
Это называется PopulatorPreprocessor.processLinearSurface(surface).
Поверхностные счетчики.
С НЕЛИНЕЙНЫМИ МАТЕРИАЛАМИ в MRUnit
Поверхностные счетчики. НЕИЗВЕСТНО и не будет увеличено.
За некоторыми исключениями конфигурация аналогична первой оценке.
1. Указание типа дороги в наших входных результатах.
RoadSurface rs = new (Enums. RoadType. ROAD) RoadSurface;
2. Установка уровня отладки log4j.
Интересно, что мы хотим подсчитывать дорожное покрытие в нашем исходном коде только тогда, когда установлен уровень отладки в логгере log4j. Сначала мы сохраняем исходный уровень ведения журнала, чтобы проверить это, затем восстанавливаем корневой регистратор и устанавливаем уровень на
ТОЛЬКО ОТЛАДКА
Уровень OriginalLevel = Logger.getRootLogger().getLevel);;);
Logger.getRootLogger().setLevel(Уровень. ОТЛАДКА) (Уровень. ОТЛАДКА)
Мы возвращаемся к исходному уровню ведения журнала в конце теста, чтобы не влиять на другие тесты.
Logger.getRootLogger().setLevel(originalLevel);(originalLevel);)
Проверим работоспособность еще раз. Поверхностные счетчики. Один раз увеличиваются значения ROADS и SurfaceCounters. SurfaceCounters и NONLINEARS.
НЕИЗВЕСТНО
Прибавок нет.
Assert.assertEquals("ДОРОГИ считаются ошибочными.," 1, 1 , 1, 1 , 1, assertEquals
MapDriver.getCounters().roads.getValue()).findCounter(SurfaceCounters. ДОРОГИ);;
Assert.assertEquals("Неверный подсчет NONLINEARS., "0, 0, 0", неверный подсчет NONLINEARS.
MapDriver.getCounters().findCounter(NONLINEARS.SurfaceCounters).getValue));;);
Assert.assertEquals("Неверный неизвестный счетчик., "0 , 0", Неверный неизвестный счетчик
MapDriver.getCounters().findCounter(.UNKNOWN).getValue));;;);
Убедитесь, что, используя следующий синтаксис PowerMock/Mockito, PopulatorPreprocessor.processLinearSurface(surface) был назван один раз.
(Мокито.раз(1)); PowerMockito.verifyStatic;
PreprocessorPopulator.processLinearSurface(rs);
REDUCER проверяет MRUnit
Как и при тестировании маппера, будут применяться те же правила. Разница в том, что, как показано ниже, мы хотели бы создать ReducerDriver и заполнить его нашим изучаемым классом редуктора.
Уменьшить Драйвер; private ReduceDriver ‹ LongWritable, BytesWritable, LongWritable, BytesWritable ›
От @Before
Настройка) ({public void setUp)
Редуктор для MyReducer = новый MyReducer);
ReduceDriver = новый ReduceDriver ‹ LongWritable, BytesWritable, BytesWritable, LongWritable ›);
Редуктор Driver.setReducer(редуктор);
}
Зависимости в MAVEN Pom в MRUnit
Помимо JUnit 4, в ваш maven pom.xml вам нужно будет добавить следующие зависимости. Обратите внимание на вспомогательные версии Mockito на веб-странице PowerMock.
‹ зависимости ›
‹ groupId › org.apache.mrunit‹/groupId › ‹ groupId›-
‹ ArtifactId › mrunit‹/artifactId › ‹ ArtifactId›-
‹ версия›0.8.0-инкубация‹/версия›-инкубация
‹ объем › испытание‹/объем ›
‹ /зависимости ›
‹ зависимости ›
‹ groupId › org.mockito‹/groupId › Org.mockito
‹ ArtifactId › mockito-all‹/artifactId› mockito-all
‹ выпуск›1.9.0-rc1‹/выпуск ›
‹ объем › испытание‹/объем ›
‹ /зависимости ›
‹ зависимости ›
‹ groupId › org.powermock‹/groupId › org.powermock
‹ ArtifactId › powermock-module-junit4‹/artifactId › junit4‹/artifactId ›
‹ выпуск›1.4.12‹/версия ›
‹ объем › испытание‹/объем ›
‹ /зависимости ›
‹ зависимости ›
‹ groupId › org.powermock‹/groupId › org.powermock
‹ ArtifactId › powermock-api-mockito‹/artifactId › Powermock-api-mockito
‹ выпуск›1.4.12‹/версия ›
‹ объем › испытание‹/объем ›
‹ /зависимости ›
Eclipse работает в MRUnit
Как и любой другой тест JUnit, будет выполнен тест. Пример теста, запущенного в Eclipse, находится здесь.
Заключение
Для разработки через тестирование MRUnit предлагает мощный и легкий подход. Хорошим побочным эффектом является то, что это помогает перейти к лучшему, чем это было возможно ранее, покрытию кода. Вы можете узнать больше в разделе Обучение Hadoop для работы с большими данными.