Настроить
Для начала загрузите MRUnit отсюда . После того, как вы извлекли файл tar, перейдите в каталог mrunit-0.9.0-инкубационный / lib. Там вы должны увидеть следующее:
- mrunit-0.9.0-инкубирования-hadoop1.jar
- mrunit-0.9.0-инкубирования-hadoop2.jar
Как я уверен, можно догадаться, что mrunit-0.9.0 -ugating-hadoop1.jar предназначен для MapReduce версии 1 Hadoop, а mrunit-0.9.0-инкубационный-hadoop2.jar предназначен для работы с новой версией Hadoop MapReduce. Для этого поста и для всех остальных мы будем использовать версию hadoop-2.0 из релиза Cloudera CDH4.1.1, поэтому нам понадобится файл mrunit-0.9.0-инкубационный-hadoop2.jar. Я добавил MRUnit, JUnit и Mockito в качестве библиотек в Intellij (JUnit и Mockito находятся в том же каталоге, что и файлы JAR MRUnit). Теперь, когда мы настроили наши зависимости, давайте начнем тестирование.
Тестирование Mappers
Настройка для тестирования маппера очень проста и лучше всего объяснить, посмотрев сначала на некоторый код. Мы будем использовать пример объединения in-mapper из предыдущего поста :
1
2
3
4
5
6
7
8
|
@Test public void testCombiningMapper() throws Exception { new MapDriver<LongWritable,Text,Text,TemperatureAveragingPair>() .withMapper( new AverageTemperatureCombiningMapper()) .withInput( new LongWritable( 4 ), new Text(temps[ 3 ])) .withOutput( new Text( '190101' ), new TemperatureAveragingPair(- 61 , 1 )) .runTest(); } |
Обратите внимание на свободный стиль API, который добавляет простоту создания теста. Чтобы написать свой тест, вы бы:
- Создание экземпляра класса MapDriver, параметризованного точно так же, как и тестируемый сопоставитель.
- Добавьте экземпляр Mapper, который вы тестируете, в вызове withMapper.
- При вызове withInput передайте ключ и входное значение, в этом случае LongWritable с произвольным значением и текстовый объект, содержащий строку из набора погодных данных NCDC, содержащегося в массиве String с именем ‘temps’, который был установлен ранее в тест (здесь не отображается, так как это отнимает от презентации).
- Укажите ожидаемый результат в вызове withOutput, здесь мы ожидаем объект Text со значением «190101» и объект TemperatureAveragingPair, содержащий значения -61 (температура) и 1 (количество).
- Последний вызов runTest передает указанные входные значения в маппер и сравнивает фактические выходные данные с ожидаемыми выходными данными, установленными в методе withOutput.
Следует отметить, что MapDriver допускает только один ввод и вывод для каждого теста. Вы можете вызывать withInput и withOutput несколько раз, если хотите, но MapDriver перезапишет существующие значения новыми, так что вы будете когда-либо тестировать только один ввод / вывод в любое время. Чтобы указать несколько входных данных, мы бы использовали MapReduceDriver, который был описан позже в нескольких разделах, но далее мы тестируем редуктор.
Тестирование редукторов
Тестирование редуктора происходит по той же схеме, что и тест картографа. Опять же, давайте начнем с примера кода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Test public void testReducerCold(){ List<TemperatureAveragingPair> pairList = new ArrayList<TemperatureAveragingPair>(); pairList.add( new TemperatureAveragingPair(- 78 , 1 )); pairList.add( new TemperatureAveragingPair(- 84 , 1 )); pairList.add( new TemperatureAveragingPair(- 28 , 1 )); pairList.add( new TemperatureAveragingPair(- 56 , 1 )); new ReduceDriver<Text,TemperatureAveragingPair,Text,IntWritable>() .withReducer( new AverageTemperatureReducer()) .withInput( new Text( '190101' ), pairList) .withOutput( new Text( '190101' ), new IntWritable(- 61 )) .runTest(); } |
- Тест начинается с создания списка объектов TemperatureAveragingPair, которые будут использоваться в качестве входных данных для редуктора.
- Создается экземпляр ReducerDriver, и, как и MapperDriver, параметры точно соответствуют тестируемому редуктору.
- Далее мы передаем экземпляр редуктора, который мы хотим протестировать в вызове withReducer.
- В вызове withInput мы передаем ключ «190101» и объект pairList, созданный в начале теста.
- Далее мы указываем вывод, который мы ожидаем, что наш редуктор испустит, тот же ключ «190101» и IntWritable, представляющий среднее значение температур в списке.
- Наконец, вызывается runTest, который подает нашему редуктору указанные входные данные и сравнивает выходные данные редуктора с ожидаемыми выходными данными.
ReducerDriver имеет то же ограничение, что и MapperDriver, — не принимать более одной пары вход / выход. До сих пор мы тестировали Mapper и Reducer по отдельности, но мы также хотели бы протестировать их вместе в интеграционном тесте. Интеграционное тестирование может быть выполнено с использованием класса MapReduceDriver. MapReduceDriver также является классом, который используется для тестирования использования объединителей, пользовательских счетчиков или пользовательских разделителей.
Интеграционное тестирование
Для проверки совместной работы вашего преобразователя и преобразователя MRUnit предоставляет класс MapReduceDriver. Класс MapReduceDriver, как и следовало ожидать, с двумя основными отличиями. Сначала вы параметризуете типы ввода и вывода преобразователя, а также типы ввода и вывода редуктора. Поскольку типы выходных данных маппера должны соответствовать типам входов редуктора, вы получите 3 пары параметризованных типов. Во-вторых, вы можете предоставить несколько входов и указать несколько ожидаемых выходов. Вот наш пример кода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
@Test public void testMapReduce(){ new MapReduceDriver<LongWritable,Text, Text,TemperatureAveragingPair, Text,IntWritable>() .withMapper( new AverageTemperatureMapper()) .withInput( new LongWritable( 1 ), new Text(temps[ 0 ])) .withInput( new LongWritable( 2 ), new Text(temps[ 1 ])) .withInput( new LongWritable( 3 ), new Text(temps[ 2 ])) .withInput( new LongWritable( 4 ), new Text(temps[ 3 ])) .withInput( new LongWritable( 5 ), new Text(temps[ 6 ])) .withInput( new LongWritable( 6 ), new Text(temps[ 7 ])) .withInput( new LongWritable( 7 ), new Text(temps[ 8 ])) .withInput( new LongWritable( 8 ), new Text(temps[ 9 ])) .withCombiner( new AverageTemperatureCombiner()) .withReducer( new AverageTemperatureReducer()) .withOutput( new Text( '190101' ), new IntWritable(- 22 )) .withOutput( new Text( '190102' ), new IntWritable(- 40 )) .runTest(); } |
Как видно из приведенного выше примера, настройка такая же, как у классов MapDriver и ReduceDriver. Вы передаете экземпляры картографа, редуктора и, необязательно, комбайнера для тестирования. MapReduceDriver позволяет передавать несколько входов с разными ключами. Здесь массив ‘temps’ — это тот же массив, на который ссылаются в образце картографа, и содержит несколько строк из набора данных погоды NCDC, а ключами в этих строках выборки являются месяцы январь и февраль 1901 года, представленные как «190101» и « 190102 ″ соответственно. Этот тест успешен, поэтому мы получаем немного больше уверенности в правильности совместной работы нашего картографа и редуктора.
Вывод
Надеемся, мы доказали, насколько полезным может быть MRUnit для тестирования программ Hadoop. Я хотел бы завершить этот пост некоторыми своими наблюдениями. Хотя MRUnit делает модульное тестирование простым для кода мапперов и редукторов, представленные здесь примеры мапперов и редукторов довольно просты. Если ваша карта и / или сокращенный код начинают становиться более сложными, вероятно, лучше отделить код от инфраструктуры Hadoop и самостоятельно протестировать новые классы. Кроме того, поскольку класс MapReduceDriver полезен для тестирования интеграции, очень легко добраться до точки, когда вы больше не тестируете свой код, а саму платформу Hadoop, что уже было сделано. Я разработал собственную стратегию тестирования, которую я намерен использовать в будущем:
- Модульное тестирование карты / уменьшение кода.
- Возможно написать один интеграционный тест с классом MapReduceDriver.
- В качестве проверки работоспособности запустите задание MapReduce при установке на один узел (на моем ноутбуке), чтобы убедиться, что оно выполняется на платформе Hadoop.
- Затем запустите мой код на тестовом кластере на EC2, используя Apache Whirr в моем случае.
Рассказ о том, как настроить установку одного узла на моем ноутбуке (OSX Lion) и установка кластера на EC2 с помощью Whirr, сделает этот пост слишком длинным, поэтому я расскажу об этих темах в следующем. Спасибо за ваше время.
Ресурсы
- Интенсивная обработка данных с MapReduce Джимми Лином и Крисом Дайером
- Hadoop: полное руководство Тома Уайта
- Исходный код из блога
- Hadoop API
- MRUnit для модульного тестирования Apache Hadoop map уменьшить количество рабочих мест
- Project Gutenberg — отличный источник книг в текстовом формате, отлично подходящий для локального тестирования заданий Hadoop.
Ссылка: Тестирование программ Hadoop с MRUnit от нашего партнера по JCG Билла Бекака в блоге Randomечет о кодировании .