We need a Mock Hive Server for tests

以前、thrift_erlを使ってHiveクライアントを作成しました。このHiveクライアントのテストを書こうとすると、Hive Serverが必要になります。テスト実行時に利用可能なHive Serverがあるとは限りません。そこで今回は、thrift_erlを使ってHive Serverのモックを作ってみました。

Thrift Code generation

今回も、.thriftファイルから生成したコードを使います。コードの生成については以下のページに記載しています。

Implementation for services

まず、Hive Serverのどのserviceを実装するか決めます。今回は、テストコードから呼び出している以下の4メソッドにしました。

次にhandle_functionという関数を用意し、service毎に返したいレスポンスを用意します。

test/hive_server_mock.erl:

 1-module(hive_server_mock).
 2
 3-export([start/0, stop/0, handle_function/2]).
 4
 5-include_lib("eunit/include/eunit.hrl").
 6-include("queryplan_types.hrl").
 7
 8start() ->
 9    {ok, _} = thrift_socket_server:start([{name, mock_server}, {port, 10000}, {service, thriftHive_thrift}, {handler, hive_server_mock}]),
10    ok.
11
12stop() ->
13    thrift_socket_server:stop(mock_server).
14
15handle_function(Function, Params) ->
16    ?debugVal(Function),
17    ?debugVal(Params),
18    case Function of
19    execute ->
20        {reply, Params};
21	fetchOne ->
22	    {reply, "a\t1"};
23	fetchAll ->
24	    {reply, ["a\t1", "b\t2", "c\t3", "d\t1", "e\t2", "f\t3", "a\t4", "b\t5"]};
25	getQueryPlan ->
26            {reply, #queryPlan{queries = [], done = true, started = true}}
27    end.

これで実装は完了です。

Starting and Stoping server in tests

あとは、テストの実行前にmockサーバを起動し、テスト完了後にmockサーバを停止します。

test/thrift_hive_tests.erl:

 1-module(thrift_hive_tests).
 2-include_lib("eunit/include/eunit.hrl").
 3-export([start/0]).
 4-export([stop/1]).
 5
 6fetchOne_test_() ->
 7    {timeout, 1200,
 8        {setup, fun start/0, fun stop/1,
 9            ?_assertEqual(
10                {ok, "a\t1"},
11                begin thrift_hive:fetch_one("select * from test") end
12            )}
13    }.
14
15fetchAll_test_() ->
16    {timeout, 1200,
17        {setup, fun start/0, fun stop/1,
18            fun() ->
19                {ok, Lst} = thrift_hive:fetch_all("select * from test"),
20                ?assertEqual(["a\t1", "b\t2", "c\t3", "d\t1", "e\t2", "f\t3", "a\t4", "b\t5"], Lst)
21            end}
22    }.
23
24getClusterStatus_test_() ->
25    {timeout, 1200,
26        {setup, fun start/0, fun stop/1,
27            fun() ->
28                {ok, Lst} = thrift_hive:fetch_all_async("select c1, count(*) from test group by c1"),
29                %?debugVal(Lst),
30                {ok, Lst}
31        end}
32    }.
33
34start() ->
35    ok = application:start(gproc),
36    ok = application:start(econfig),
37    ok = econfig:register_config(erl_shib, ["../erl_shib.ini"], [autoreload]),    
38    true = econfig:subscribe(erl_shib),
39    ok = hive_server_mock:start().
40
41stop(_Result) ->
42    ok = hive_server_mock:stop(),
43    ok = application:stop(econfig),
44    ok = application:stop(gproc),
45    ok.

Conclusion

Mock Hive Serverを作成し、Hive Serverが無くてもHiveクライアントのテストを動かせるようになりました。

並列でテストを実行することを考えると、サーバの起動/停止は全テストの前後に実行する必要があります。

また、実際のHive Serverは、fetchOneやfetchAllを呼び出した際に、直前に実行したexecuteの実行結果を参照することになるので、サーバ側で状態を保持してるものと思われます。今回のコードではこの部分がない為、executeで指定したHQL毎に実行結果を変更することができません。このあたりはもう少し調べてみたいと思います。