LoginSignup
2
1

More than 3 years have passed since last update.

C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 2.CUnit導入 ~

Last updated at Posted at 2021-02-03

概要

アジャイル開発とテスト駆動開発の関係について理解できたので、ここからは実践です。
今回は、CUnitを導入し実行していくところまで行こうと思います。

記事の全体像

環境

OS : Linux ubuntu 18.04.5 LTS
テストツール : CUnit 2.1-3

コンパイラ : gcc 7.5.0

xUnitとは

xUnit とは、ソフトウェアの単体テスト(Unit Test)を行うためのフレームワークの総称です。
これらを使うことで、ソフトウェアの様々な要素を簡単にテストすることが可能になります。
本記事で扱うCUnit(C)以外にも、JUnit(Java), PyUnit(Python), PHPUnit(PHP)等様々なフレームワークが存在します。

xUnitを導入するメリット

  • テストが自動化できる
  • 同じテストを何度も書かなくて良い
  • ここのテスト結果がどうあるべきかを覚えなくて良い

CUnit導入

では早速CUnitを導入して行きましょう。

CUnitのURL

最新版が2.1-3ですので、2.1-3を下記コマンドでインストールします。
環境が異なる方は適宜インストールして展開してください。

Bash
wget http://jaist.dl.sourceforge.net/project/cunit/CUnit/2.1-3/CUnit-2.1-3.tar.bz2

フォルダを解凍します。

Bash
tar xvf ./CUnit-2.1-3.tar.bz2
cd CUnit-2.1-3/

解凍したフォルダの中のREADMEを参考にしながら進めます。

README
Linux:

  In addition to jam, the standard GNU build system is still supported.
  The usual sequence of steps should succeed in building and installing CUnit:
    1. aclocal  (if necessary)
    2. autoconf (if necessary)
    3. automake (if necessary)
    4. chmod u+x configure (if necessary)
    5. ./configure --prefix <Your choice of directory for installation>
    6. make
    7. make install

  What's installed:
    1. libcunit.a (Library file)
    2. CUnit Header files
    3. DTD and XSL files supporting xml output files in share directory
    4. Man Pages in relevant man directories under the installation path.
    5. HTML users guide in the doc subdirectory of the installation path.
    6. Example & test programs in the share subdirectory of the install path.

分かりづらかったため、エラー発生時のみ実行結果も載せています。

フォルダ内に aclocal.m4があるので、 aclocalコマンドは不要かもしれないです。
ワーニングなので、とりあえず進めます。

Bash
$ aclocal
aclocal: warning: autoconf input should be named 'configure.ac', not 'configure.in'

autoconfでエラーが出ました。
エラー内容ぐぐると、libtoolをインストールしろとのこと。

Bash
$ autoconf
configure.in:161: error: possibly undefined macro: AC_PROG_LIBTOOL
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.

$ sudo apt install libtool -y

autoconfでエラー出た場合は、autoreconfが必要らしい。。。

Bash
$ autoreconf -i

もう一度autoconfから

Bash
$ autoconf
$ automake
$ ./configure
$ make
$ sudo install

環境変数LD_LIBRARY_PATHを設定する。
永続化させる場合は、同様の記述を.bashrcに記述してください。

(Bash)or(.bashrc)
export LD_LIBRARY_PATH=/usr/local/lib

.bashrcの場合は反映を忘れずに

Bash
source ~/.bashrc

テストの記述

いよいよテスト駆動開発を始めます。
CUnit User Guideを適宜確認しながら記述していきます。

CUnitの一般的な流れは下記の様な流れになります。

  1. テスト関数の記述(suite, init/cleanup が必要な場合も記述)
  2. テスト構造を初期化 (CU_initialize_registry())
  3. テストスイートの追加(CU_add_suite())
  4. テスト関数の追加 (CU_add_test())
  5. 適切なインターフェースを使用してテストを実行。(例)CU_console_run_tests)
  6. テスト構造体のクリーン (CU_cleanup_registry())

テスト関数を下記のように記述しました。

unitTest.c

#include <CUnit/CUnit.h>
#include <CUnit/Console.h>

// バグのあるadd関数(テスト対象)
int add(int x, int y) {
    return 0;
}

/**
 * addテストスイート
 **/
// テスト関数001
void test_add_001(void) {
    CU_ASSERT_EQUAL(add(1, 2), 3);
}

// テスト関数002
void test_add_002(void) {
    CU_ASSERT_EQUAL(add(-1, -2), -3);
}

// main関数
int main() {
CU_pSuite add_suite;

    CU_initialize_registry(); // 2.テスト構造の初期化
    add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
    CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
    CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加

    CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行
    CU_cleanup_registry(); // 6.テスト構造のクリーン
}

ビルドと実行

さあ、ビルドしましょう

Bash
$ gcc unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest
$ ./unitTest

     CUnit - A Unit testing framework for C - Version 2.1-3
             http://cunit.sourceforge.net/


***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un  (S)elect  (L)ist  (A)ctivate  (F)ailures  (O)ptions  (H)elp  (Q)uit
Enter command: r

Running Suite : add
     Running Test : test_001
     Running Test : test_002

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      2      2      0      2        0
             asserts      2      2      0      2      n/a

Elapsed time =    0.000 seconds


***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un  (S)elect  (L)ist  (A)ctivate  (F)ailures  (O)ptions  (H)elp  (Q)uit
Enter command: 

上手く行った。やったぜ〜。

おまけ

テスト対象を別ファイルに分割する

上手くCUnitを導入できました。
でも、現状はテストコードとテスト対象コードが同じファイルに存在しています。
これでは実用的ではないですね。
なので、分割します。

unitTest.c内で定義していたadd()add.cに抽出し、
add()の定義をadd.hに持ってきました。

add.c
#include "add.h"

// バグのあるadd関数(テスト対象)
int add(int x, int y) {
        return 0;
}
add.h
// #ifndef ADD_H_
// #define ADD_H_

int add(int x, int y);

//  #endif /* ADD_ */
unitTest.c
#include <CUnit/CUnit.h>
#include <CUnit/Console.h>

#include "add.h"

/**
 * addテストスイート
 **/
// テスト関数001
void test_add_001(void) {
    CU_ASSERT_EQUAL(add(1, 2), 3);
}

// テスト関数002
void test_add_002(void) {
    CU_ASSERT_EQUAL(add(-1, -2), -3);
}

// main関数
int main() {
CU_pSuite add_suite;

    CU_initialize_registry(); // 2.テスト構造の初期化
    add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
    CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
    CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加

    CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行
    CU_cleanup_registry(); // 6.テスト構造のクリーン
}

もう一度コンパイルします。

Bash
gcc add.c unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest 

実行

Bash
$ ./unitTest
     CUnit - A Unit testing framework for C - Version 2.1-3
             http://cunit.sourceforge.net/


***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un  (S)elect  (L)ist  (A)ctivate  (F)ailures  (O)ptions  (H)elp  (Q)uit
Enter command: r

Running Suite : add
     Running Test : test_001
     Running Test : test_002

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      2      2      0      2        0
             asserts      2      2      0      2      n/a

Elapsed time =    0.000 seconds

テストを自動実行にする

いい感じになってきました。
あとは、テストを対話型ではなく、自動実行にしたいです。
そのためには、unitTest.cの中身を少し弄ります。

unitTest.c
#include <CUnit/CUnit.h>
// #include <CUnit/Console.h> ★ココ
#include <CUnit/Automated.h> ★ココ

#include "add.h"

/**
 * addテストスイート
 **/
// テスト関数001
void test_add_001(void) {
    CU_ASSERT_EQUAL(add(1, 2), 3);
}

// テスト関数002
void test_add_002(void) {
    CU_ASSERT_EQUAL(add(-1, -2), -3);
}

// main関数
int main() {
CU_pSuite add_suite;

    CU_initialize_registry(); // 2.テスト構造の初期化
    add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
    CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
    CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加

    CU_automated_run_tests(); // ★ココ
    // CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行 ★ココ
    CU_cleanup_registry(); // 6.テスト構造のクリーン
}

さっそく実行しましょう

Bash
gcc add.c unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest
./unitTest

実行すると、CUnitAutomated-Results.xmlという名前のxmlファイルが吐き出されます。
これを後工程のJenkinsで利用予定です。

CUnitドキュメント

今回の記事を書く上で使用した、CUnitのドキュメントへのリンクの一覧を貼って起きます。

  • CUnitのホームページ (CUIからでもダウンロード可)
  • CUnitのドキュメント こちらのページにCUnitの使用について大体のことが書いてあります。
  • CUnitのドキュメント doxygenにて生成されたドキュメントです
  • README ダウンロードしたファイルを解凍したフォルダの中に格納されています。インストール関連について書かれています。

次回

今回までで、何故テスト駆動で開発を進めていくのかについて説明し、xUnitによるテストを導入してきました。
これでいつでもテストを走らせる環境が整いそうです。
でも、まだ手動の箇所が残ってますね。
そうです。ビルド工程です。
次回はこの部分をCMakeによって自動化していきます。

参考文献

CUnit User Guide
CUnitでCプログラムの単体テストをする
CUnitについての備忘録
gcc コンパイルオプション備忘録

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1