hiveのクエリーの単体テストのやり方を調べてみると、
単体検査用のhive実行環境の立ち上げを含めてまるっとやってくれる、
HiveQLUnitというフレームワークがあったので、試してみました。
このエントリでは、
HiveQLUnitを使って単体テストを実施する手順を説明します。

HiveQLUnit
http://finraos.github.io/HiveQLUnit/

全体の流れは、以下の通りです。

  • テスト用プロジェクト作成
  • テストコードとテストデータの準備
  • テストの実行 (コマンドラインから)
  • テストの実行 (IntelliJから)

jdkとmaven3はセットアップ済みの前提です。
私が使ったバージョンは以下の通り。

  • JDK 1.8.0_71
  • Apache Maven 3.3.9

このエントリで作成したgithubリポジトリ:
https://github.com/takemikami/hiveqlunit-sample

# このリポジトリをcloneして、
# mvn testってすればとりあえずテストは動くはず。

テスト用プロジェクト作成

まずは、テスト用プロジェクトのディレクトリ作成とpom.xmlを準備します。

以下のように、プロジェクトとテストコード用のディレクトリを作成します。

$ mkdir hiveqlunit-sample && cd $_
$ mkdir -p src/test/{java,resources}

以下のとおり、pomファイルを作成します。

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.takemikami</groupId>
  <artifactId>hiveqlunit-sample</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>hiveqlunit sample</name>
  <url>https://github.com/takemikami/hiveqlunit-sample</url>
  <dependencies>
    <dependency>
        <groupId>org.finra.hiveqlunit</groupId>
        <artifactId>hiveQLUnit</artifactId>
        <version>1.0</version>
        <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.18.1</version>
        <configuration>
          <argLine>-XX:MaxPermSize=128m -Dhive.metastore.warehouse.dir=${project.basedir}/target/hive-warehouse</argLine>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <filesets>
            <fileset>
              <directory>metastore_db</directory>
            </fileset>
          </filesets>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

テストコードとテストデータの準備

以下の通り、テスト対象のHQLコードを用意します。

src/main/resources/sample.hql

create table sales_by_shop
as select shop, sum(amount) amount
from sales
group by shop

以下の通り、テストコードを用意します。
このコードでは以下の処理を実施します。

  • 入力テーブルに、テスト用データを読み込む
  • HQLを読み込んで実行する
  • 出力テーブルの内容に対するテストを実行する

src/test/java/SampleTest.java

import static org.junit.Assert.assertEquals;

import org.apache.spark.sql.Row;
import org.apache.spark.sql.hive.HiveContext;
import org.finra.hiveqlunit.resources.LocalFileResource;
import org.finra.hiveqlunit.resources.TextLiteralResource;
import org.finra.hiveqlunit.rules.SetUpHql;
import org.finra.hiveqlunit.rules.TestDataLoader;
import org.finra.hiveqlunit.rules.TestHiveServer;
import org.finra.hiveqlunit.script.MultiExpressionScript;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;

import java.io.*;

public class SampleTest {

    @ClassRule
    public static TestHiveServer hiveServer = new TestHiveServer();

    @Rule
    public static TestDataLoader loader = new TestDataLoader(hiveServer);

    @Test
    public void testA() throws Exception {
        HiveContext sqlContext = hiveServer.getHiveContext();
        sqlContext.sql("drop table if exists sales");
        sqlContext.sql("create table sales(saletime timestamp, shop string, amount int) row format delimited fields terminated by '\t' lines terminated by '\n'");
        loader.loadDataIntoTable("sales", new LocalFileResource("src/test/resources/testdata.txt"), "");

        sqlContext.sql("drop table if exists sales_by_shop");
        sqlContext.sql(readHql("sample.hql"));

        Row[] results = sqlContext.sql("SELECT shop,amount FROM sales_by_shop").collect();
        assertEquals(3, results.length);

        Row[] resultsA = sqlContext.sql("SELECT sum(amount) FROM sales_by_shop").collect();
        Row[] resultsB = sqlContext.sql("SELECT sum(amount) FROM sales").collect();
    }

    public static String readHql(String fname) throws IOException {
        StringBuffer buf = new StringBuffer();
        BufferedReader br = new BufferedReader(new FileReader(new File("src/main/resources/" + fname)));
        String str = br.readLine();
        while(str != null) {
            buf.append(str).append("\n");
            str = br.readLine();
        }
        br.close();
        return buf.toString();
    }

}

以下の通り、テストデータを用意します。(タブ区切りです)

src/test/resources/testdata.txt

2012-11-20 10:35:00 東京店   1000
2012-12-05 19:07:00 大阪店   1200
2012-12-07 12:42:00 京都店   2200
2012-12-10 13:05:00 東京店   1800

テストの実行 (コマンドラインから)

以下のように、テストを実行します。

$ mvn clean test

初回は関連するjarをダウンロードするのでかなり時間がかかります。
二回目以降も、HadoopやHiveの初期化で結構時間がかかります。

テストの実行 (IntelliJから)

テストの実行はIntellJからも実行出来るので、
作成したテスト用プロジェクトをIntellJに取り込みます。

プロジェクトの取り込み

IntellJのメニューから、File→Openを選び、
作成したテスト用プロジェクトのフォルダを選択してOKを押下します。

しばらく待つとプロジェクトがIntelliJniインポートされます.

テストの実行

左側プロジェクトのツリーから「src/test/java/SampleTest.java」を選び、
コンテキストメニューから「Run SampleTest」を選択します。

テストが実行されて、以下のように結果が表示されます。

hiveqlunit01