Для заданий 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 для работы с большими данными.