act-act about projects rss

How to create Mock Hive Server

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メソッドにしました。

  • execute
  • fetchOne
  • fetchAll
  • getQueryPlan

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

test/hive_server_mock.erl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-module(hive_server_mock).

-export([start/0, stop/0, handle_function/2]).

-include_lib("eunit/include/eunit.hrl").
-include("queryplan_types.hrl").

start() ->
    {ok, _} = thrift_socket_server:start([{name, mock_server}, {port, 10000}, {service, thriftHive_thrift}, {handler, hive_server_mock}]),
    ok.

stop() ->
    thrift_socket_server:stop(mock_server).

handle_function(Function, Params) ->
    ?debugVal(Function),
    ?debugVal(Params),
    case Function of
    execute ->
        {reply, Params};
	fetchOne ->
	    {reply, "a\t1"};
	fetchAll ->
	    {reply, ["a\t1", "b\t2", "c\t3", "d\t1", "e\t2", "f\t3", "a\t4", "b\t5"]};
	getQueryPlan ->
            {reply, #queryPlan{queries = [], done = true, started = true}}
    end.

これで実装は完了です。

Starting and Stoping server in tests

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

test/thrift_hive_tests.erl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
-module(thrift_hive_tests).
-include_lib("eunit/include/eunit.hrl").
-export([start/0]).
-export([stop/1]).

fetchOne_test_() ->
    {timeout, 1200,
        {setup, fun start/0, fun stop/1,
            ?_assertEqual(
                {ok, "a\t1"},
                begin thrift_hive:fetch_one("select * from test") end
            )}
    }.

fetchAll_test_() ->
    {timeout, 1200,
        {setup, fun start/0, fun stop/1,
            fun() ->
                {ok, Lst} = thrift_hive:fetch_all("select * from test"),
                ?assertEqual(["a\t1", "b\t2", "c\t3", "d\t1", "e\t2", "f\t3", "a\t4", "b\t5"], Lst)
            end}
    }.

getClusterStatus_test_() ->
    {timeout, 1200,
        {setup, fun start/0, fun stop/1,
            fun() ->
                {ok, Lst} = thrift_hive:fetch_all_async("select c1, count(*) from test group by c1"),
                %?debugVal(Lst),
                {ok, Lst}
        end}
    }.

start() ->
    ok = application:start(gproc),
    ok = application:start(econfig),
    ok = econfig:register_config(erl_shib, ["../erl_shib.ini"], [autoreload]),    
    true = econfig:subscribe(erl_shib),
    ok = hive_server_mock:start().

stop(_Result) ->
    ok = hive_server_mock:stop(),
    ok = application:stop(econfig),
    ok = application:stop(gproc),
    ok.

Conclusion

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

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

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