テスト

アプリの開発の過程において、テストという作業は欠かせないものです。同じようなことを繰り返し確認するテストは退屈なものなので、自動化できるところはそうしてしまいましょう。

モデルのユニットテスト

本節では、モデルが正しく動作するかチェックしてみます。テストフレームワークは Qt 付属の TestLib を踏襲しているので、そちらのドキュメントも一度ご覧ください。

チュートリアルで作成した Blog モデルのテストコードを作ってみましょう。あらかじめ、モデルの共有ライブラリは作成しておいてください。
まず、test ディレクトリに作業ディレクトリを作成します。

 $ cd test
 $ mkdir blog
 $ cd blog

Blog モデルの生成と読込みのテストケースを作ってみます。
テストを実施するクラス名は TestBlog とします。次のような内容のソースコードを testblog.cpp というファイル名で保存します。

#include <TfTest/TfTest>
#include "models/blog.h"    // モデルクラスのインクルード

class TestBlog : public QObject
{
    Q_OBJECT
private slots:
    void create_data();
    void create();
};

void TestBlog::create_data()
{
    // テストデータの定義
    QTest::addColumn<QString>("title");
    QTest::addColumn<QString>("body");

    // テストデータに追加
    QTest::newRow("No1") << "Hello" << "Hello world.";
}

void TestBlog::create()
{
    // テストデータの取込
    QFETCH(QString, title);
    QFETCH(QString, body);

    // テストのロジック
    Blog created = Blog::create(title, body);
    int id = created.id();
    Blog blog = Blog::get(id);  // IDでモデルを取得

    // 実行結果の検証
    QCOMPARE(blog.title(), title);
    QCOMPARE(blog.body(), body);
}

TF_TEST_MAIN(TestBlog)   // 作成したクラス名を指定
#include "testblog.moc"      // おまじない。拡張子を .moc にする

補足すると、この中でテストを実行するのは create() メソッドであり、実際に戻り値を検証するのは QCOMPARE マクロです。create_data() メソッドは、テストデータを create() メソッドへ渡す役目をします。メソッド名の末尾には ‘_data’ をつけるのがルールです。

この例で、create_data() メソッドでは次の処理を行なっています。

  • QTest::addColumn() 関数で、テストデータの型と名前を定義する
  • QTest::newRow() 関数で、テストデータに追加する

create() メソッドでは次の処理を行なっています。

  • テストデータを取り込む
  • テストロジックを実行する
  • その実行結果が正しいか検証する

次に、Makefile を作成するためのプロジェクトファイルを作ります。ファイル名は testblog.pro とし、次の内容を保存します。

 TARGET = testblog
 TEMPLATE = app
 CONFIG += console debug c++11
 CONFIG -= app_bundle
 QT += network sql testlib
 QT -= gui
 DEFINES += TF_DLL
 INCLUDEPATH += ../..
 LIBS += -L../../lib -lmodel
 include(../../appbase.pri)
 SOURCES = testblog.cpp      # ファイル名を指定する

プロジェクトファイルを保存したら、そのディレクトリで次のコマンドを実行してバイナリを作成します。

 $ qmake
 $ make

次に、テストを実行するための設定を行います。
テストコマンドは各種設定ファイルを参照する必要があるため、その直下に位置するよう config ディレクトリへのシンボリックリンクを作成します。データベースとして SQLite を使用している場合は db ディレクトリへのシンボリックリンクも作成します。

 $ ln -s  ../../config  config
 $ ln -s  ../../db  db

Windows の場合、テストの EXE ファイルは debug ディレクトリに作られるので、そこでシンボリックリンクを作ります。「ショートカット」ではないので注意してください。シンボリックリンクを作るには、管理者権限で起動したコマンドプロンプトからコマンドを実行する必要があります。

 > cd debug
 > mklink /D  config  ..\..\..\config
 > mklink /D  db  ..\..\..\db

さらに、Blogモデルを包含する共有ライブラリへパスを通します。
Linux の場合には、次のように環境変数を設定します。

 $ export  LD_LIBRARY_PATH=/path/to/blogapp/lib

Windows の場合には、PATH 変数に設定を追加します。

 > set PATH=C:\path\to\blogapp\lib;%PATH%

次に、データベースの接続情報を確認をします。ユニットテストでは、データベース設定ファイル(database.ini)にある test セクションの接続情報が使用されます。

 [test]
 DriverType=QMYSQL
 DatabaseName=blogdb
 HostName=
 Port=
 UserName=root
 Password=
 ConnectOptions=

以上で設定は完了です。それでは、テストを実行してみましょう。テストが成功すれば、次のようなメッセージが表示されます。Windows の場合には、TreeFrog Command Prompt 上で実行してください。

$ ./testblog
Config: Using QtTest library 5.5.1, Qt 5.5.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 5.4.0 20160609)
PASS   : TestBlog::initTestCase()
PASS   : TestBlog::create(No1)
PASS   : TestBlog::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestBlog *********

もし期待した結果と一致しなければ、次のようなメッセージが表示されるでしょう。

********* Start testing of TestBlog *********
Config: Using QtTest library 5.5.1, Qt 5.5.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 5.4.0 20160609)
PASS   : TestBlog::initTestCase()
FAIL!  : TestBlog::create(No1) Compared values are not the same
   Actual   (blog.body()): "foo."
   Expected (body): "Hello world."
   Loc: [testblog.cpp(35)]
PASS   : TestBlog::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestBlog *******

モデルごとにテストケースを作成し、どんどんテストを行なってください。モデルがきちんと動作するかどうかが、Web アプリの要(かなめ)となるのです。