ククログ

株式会社クリアコード > ククログ > C言語用単体テストフレームワークCutterへのHTTPテスト機能追加

C言語用単体テストフレームワークCutterへのHTTPテスト機能追加

現在クリアコードでインターン中のはやみずです。クリアコードのインターンシップ制度は今年度から始まり、最初のインターン生として2週間クリアコードで働かせていただくことになりました。今回と次回の2つの記事で、現在インターンシップで取り組んでいる内容について紹介したいと思います。今回の記事では、C言語用単体テストフレームワークCutterへのHTTPテスト機能追加について紹介します。

Cutter: C言語用単体テストフレームワーク

ククログでも度々紹介されているCutterですが、このテストフレームワークを利用することでC言語での単体テストを非常に効率良く開発することができます。

Cutterはできるだけ簡潔にテストを記述できるように、基本的な assert 系関数以外にも様々なユーティリティ関数を提供しています。また、GLibを利用したプログラムのテストを開発するためのGCutterや、gdk-pixbuf(C言語で画像を扱うためのライブラリ)用の GdkCutter Pixbufなどのモジュールが含まれています。これらを利用することで、GLib や Gdk Pixbuf を使ったプログラムはよりこれらのライブラリに特化したテストを簡単に書くことが可能となっています。これからも対応ライブラリは増えてゆくのかも?

Cutterの強みの1つに、C言語でありながらメモリ管理の手間が非常に少ないということが挙げられます。ほとんどのテストフレームワークは set up(準備)→test実行→tear down(後片付け) という処理の流れを基本としているので、テスト中に利用するオブジェクトは tear down のときに解放してやればよいことがわかっています。Cutter は「このオブジェクトは tear down 時に解放しといてね」ということを Cutter に教えるための API を提供しているため、この API を利用することで解放忘れによるメモリリークを防ぐことができます。例えば文字列であれば、cut_take_string(const gchar *string) を利用することで文字列を動的にアロケートした領域にコピーしてそのポインタを得ることができ、tear down時にはこの文字列が自動的に解放されます。

今回開発しているCutterのHTTPテスト機能も、このパターンを使ってオブジェクトを簡単に生成して、しかも勝手に解放してくれるようになっています。

SoupCutter: CutterのHTTPテストモジュール

さて、本題のHTTPテスト機能を実装した SoupCutter に話を移しましょう。今回の開発では、HTTPの機能を簡単に実装するために libsoup というライブラリを利用しました。というよりも、GLibをサポートするのが GCutter、Gdk Pixbuf をサポートする GdkCutter Pixbuf などのように、libsoup をサポートする SoupCutter という位置付けのほうが正確です。しかし、簡単なテストであれば libsoup 自体には一切触れることなく作成することができるので、Cutter で HTTP サーバーや HTTP クライアントのテストを簡単にできるようにするためのモジュールだと思っていただいても大丈夫です。

SoupCutter を使って HTTP サーバープログラムが正しくレスポンスを返しているかをテストするには、次のように書くことができます。

SoupCutClient *client = soupcut_client_new();

/* http://localhost:8080/?key=value に HTTP Request を送信 */
soupcut_client_get(client, "http://localhost:8080/",
                   "key", "value", NULL);

soupcut_client_assert_response(client);
soupcut_client_assert_equal_content_type("text/plain", client);
soupcut_client_assert_equal_body("Hello, world", client);

SoupCutClient というのは、サーバーとやりとりしたHTTPリクエスト/レスポンスを内包しているオブジェクトです。..._assert_response では、最後に受け取ったレスポンスが 2XX (200 OK など) であるかをチェックしています。同様に、..._assert_content_type では Content-Type が text/plain であることを、..._assert_equal_body ではレスポンスの本文が Hello, world であることをチェックしています。このようにして、SoupCutter を使うと非常に簡潔な記述で HTTP サーバーが思った通りに動いているかを調べることができるようになっています。

また、SoupCutter の最初の機能としては HTTP サーバーのテスト、つまりHTTPクライアントとしての機能を実装しているのですが、このHTTPクライアント機能が正しく動作しているかをテストしなければなりません。HTTPクライアント機能をテストするためには、HTTPサーバが必要です。HTTPサーバーをテストするためにHTTPクライアントを実装し、そのHTTPクライアントをテストするためにHTTPサーバーを実装する。ややこしいですね。

というわけで、SoupCutter は簡単にHTTPサーバーを作ることもできます。

static void
server_callback (SoupServer *server,
                 SoupMessage *msg,
                 const gchar *path,
                 GHashTable *query,
                 SoupClientContext *client,
                 gpointer user_data)
{
    .... /* リクエストを処理して結果を返す */
}

SoupServer *server;
server = soupcut_server_take_new(NULL);
soup_server_add_handler(server, "/", server_callback,
                        NULL, NULL);
soup_server_run(server);

HTTPサーバーの作成自体はたった3行でできてしまいました。server_callback は実際にリクエストを処理してレスポンスを生成するコールバック関数です。これを SoupServer のリクエストハンドラに追加して、soup_server_run でメインループに入り、サーバーが動き始めます。

ここで注目してほしいのは、サーバーを生成するときの soupcut_server_take_new です。Cutterでは take と名前のつく関数で生成したオブジェクトは、tear down時に自動で解放されます。HTTPサーバーの場合は、ちゃんとソケットの後処理まで行い、オブジェクトを解放してくれます。つまり、HTTPサーバーを簡単に作れるだけではなく、勝手に後片付けまでしてくれます。

まとめ

今回は、現在開発中である Cutter の HTTPテストモジュール SoupCutter について簡単に紹介しました。SoupCutterを使うと簡単便利にHTTPサーバー/クライアントのテストを作成することができるようになります。SoupCutter は現在クリアコードのインターンシップで開発していて、来週末に SoupCutter を含めた Cutter をリリースすることを目標に頑張っています。もし HTTP のテストを作る必要に迫られた場合には、SoupCutter を検討してみてください。