質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ユニットテスト

ユニットテストは、システムのテスト手法の一つで、個々のモジュールを対象としたテストの事を指します。対象のモジュールが要求や性能を満たしているか確認する為に実行します。

Q&A

解決済

4回答

25540閲覧

既存ソースに手を入れずに同一ソース内の関数をスタブにしてユニットテストを実施する方法

ttyp03

総合スコア16998

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ユニットテスト

ユニットテストは、システムのテスト手法の一つで、個々のモジュールを対象としたテストの事を指します。対象のモジュールが要求や性能を満たしているか確認する為に実行します。

0グッド

3クリップ

投稿2016/06/13 02:19

次のような条件のユニットテストを実施したいです。
0. ソースは既存であり、手を入れることはできない
0. このソースに対して関数単位のテストを実施したい
0. テスト対象となる関数から呼ばれる関数はスタブとしたい
0. スタブにする関数は、テスト対象と同一のソースに含まれる場合がある

この場合、4番目の同一ソースのスタブがネックになると思います。
既存ソースには手を入れずに同一ソースにある関数をスタブにしてユニットテストを行う方法があれば教えてください。

実は過去に実施済みで、その時は、散々調べて、試行錯誤した結果、「できない」という結論に至りました。
結局、泣く泣く既存ソースに次のようなコード(#ifdef UNITTEST~#endif)を足すことなってしまいました。

既存ソース側

C

1#ifdef UNITTEST 2#define func1 (*pfunc1) 3#define func2 (*pfunc2) 4#endif // UNITTEST 5static void test(void) 6{ 7 func1(); 8 func2(); 910} 11#undef func1 12#undef func2 13void func1(void) 14{ 1516} 17void func2(void) 18{ 1920}

テストコード側

C

1void testcode(void) 2{ 3 pfunc1 = func1_stub; 4 pfunc2 = func2_stub; 5 test(); 6}

実際にはもう少し簡潔に書けるように、インクルードファイルを作ったりしました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

むかーし、似たようなことをやったことがあります。
こんな感じではどうでしょうか。Chironianさんと同じような発想ですが、古い規格のCコンパイラーでも使えます。

テストコード側

C

1// func1を'func1_stub_行番号'に置き換える 2#define func1 func1_(__LINE__) 3#define func1_(line) func1__(line) 4#define func1__(line) func1_stub_ ## line 5 6// func2を'func2_stub_行番号'に置き換える 7#define func2 func2_(__LINE__) 8#define func2_(line) func2__(line) 9#define func2__(line) func2_stub_ ## line 10 11// test関数内でfunc1が呼ばれているのは9行目 12void func1_stub_9(void) 13{ 14 printf("func1_stub\n"); 15} 16 17// test関数内でfunc2が呼ばれているのは10行目 18void func2_stub_10(void) 19{ 20 printf("func2_stub\n"); 21} 22 23#include "test.c" 24 25int main() 26{ 27 test(); 28 return 0; 29}

既存ソース側

C

1// test.c 2#include <stdio.h> 3 4void func1(void); 5void func2(void); 6 7static void test(void) 8{ 9 func1(); 10 func2(); 11} 12 13void func1(void) 14{ 15 printf("func1()\n"); 16} 17void func2(void) 18{ 19 printf("func2()\n"); 20}

追記

スタブ関数を引数渡しにする方法。

C

1typedef void (*func_t)(void); 2#define test(a) test_stubcall(func_t func1, func_t func2) 3 4#include "test.c" 5 6void func1_stub(void) 7{ 8 printf("func1_stub\n"); 9} 10 11void func2_stub(void) 12{ 13 printf("func2_stub\n"); 14} 15 16int main() 17{ 18 test_stubcall(func1_stub, func2_stub); 19 return 0; 20}

投稿2016/06/13 10:00

編集2016/06/14 01:34
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/06/13 11:38

おお、行番号を使うのですか!! 頭のいい方法ですね。 これなら__VA_ARGS__も要らないし、バラメータがあっても対応できますし。
ttyp03

2016/06/14 00:25

おお!素晴らしいけど惜しい! 行番号を意識して書かないといけないのが惜しいですね。 例えばfunc1()をあちこちで使ってたら(例えば9行目と10行目)、 void func1_stub_9(){} と void func1_stub_10(){} を用意しないといけないのですよね。 あとテストコードを使いまわして、改修後のソースにも使いたいという場合、行番号の調整をしないといけなくなるのも手間がかかりますね。 質問では「既存ソースに手を入れない」前提で書いているので、これは目をつぶりますが。 というのを踏まえて少し改良してみました。 #define func1 func1_(__LINE__) #define func1_(line) func1__(line) #define func1__(line) func1_stub_ ## line #define func1_stub_9 func1_stub // 追加 #define func1_stub_10 func1_stub // 追加 void func1_stub(void) { printf("func1_stub\n"); } スタブ関数の数が減るだけであまりメリットはないですかね…。 行番号を意識しないといけないコードからも脱却できてませんし。 もう少しな気もするんですが難しいですね。
catsforepaw

2016/06/14 00:56

> #define func1_stub_9 func1_stub // 追加 > #define func1_stub_10 func1_stub // 追加 複数箇所での呼び出しを考慮するなら、これがベストですね。 おそらく、同じ関数を、定義する場所と呼び出す場所で名前を変えるという方法は、これ以外にはないと思います。 既存ソースをメンテナンスすることがあるのなら、こういう状況を想定した仕組みを入れ込むようにするのが良いと思います。 例えば、 // test.c #ifndef FUNC1 #define FUNC1 func1 #endif static void test(void) { FUNC1(); } void func1(void) { : } このようにしておけば、通常はfunc1を呼びますが、テストコード側でtest.cをインクルードする前にFUNC1マクロにスタブ関数名を設定すれば、それを呼ぶようにできます。
ttyp03

2016/06/14 01:05

自分で一から作るならあらかじめ仕込んでおくのですが、この案件は既にあるものに対してテストをしないといけなかったので難儀しました。
catsforepaw

2016/06/14 01:33

ひらめきました。スタブ関数をtest関数に引数として渡せば煩わしさは軽減します。 コード例は回答の方に追記します。
ttyp03

2016/06/14 02:00

試してみました。 これは…いいかもしれない。 テスト対象関数に引数があっても大丈夫のようですし、スタブを使う/使わないの制御も楽にできそうです。
guest

0

こんにちは。

制約が凄く厳しいのですが、下記のような方法があります。

C

1#include <stdio.h> 2 3void func1(void); 4void func2(void); 5 6#define func1(...) func1##__VA_ARGS__() 7#define func2(...) func2##__VA_ARGS__() 8 9// --- 既存ソース部分。実際には#includeで取り込む --- 10static void test(void) 11{ 12 func1(); 13 func2(); 14} 15 16void func1(void) 17{ 18 printf("func1()\n"); 19} 20void func2(void) 21{ 22 printf("func2()\n"); 23} 24// --- 既存ソース終わり --- 25 26#undef func1 27#undef func2 28 29void func1(void) 30{ 31 printf("func1_stub()\n"); 32} 33void func2(void) 34{ 35 printf("func2_stub()\n"); 36} 37 38int main() 39{ 40 test(); 41 return 0; 42}

__VA_ARGS__が使えることと、スタブ化したい関数がパラメータなし、かつ、定義ではvoidが記述されていることが条件ですので、かなり制約がきついです。

でちまる氏のようなプリプロセッサ・ウィザードならパラメータ付きの関数でもなんとかできるかも?

ちなみに既存ソース部分は下記のように展開されます。

C

1static void test(void) 2{ 3 func1(); 4 func2(); 5} 6 7void func1void() 8{ 9 printf("func1()\n"); 10} 11void func2void() 12{ 13 printf("func2()\n"); 14}

投稿2016/06/13 02:45

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttyp03

2016/06/13 04:47

既存ソース側の関数名を置き換えるのですね。 しかしやはりパラメーターなしという制約が厳しいですね。 あと以前の環境では__VA_ARGS__が使えなかったので、こちらもアウトです。 ちなみに環境はHEWで、コンパイラはルネサスのCコンパイラです。 あとリンクしていただいたサイトは、職場のアクセス制限で見れませんでした。すみません。
guest

0

既存のソースをコピーして手を入れたものをテストすれば良いのでは?

投稿2016/06/13 10:06

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttyp03

2016/06/14 00:30

本番に使われているソースそのものをテストしたいので、少しでも手を入れてしまうと別物扱いになるので却下です。 あと既存ソースが修正された場合、コピーして、テスト用のコードを入れて、と二度手間になってしまいますし。
Zuishin

2016/06/14 00:51

別物にならないよう工夫はいくらでもできると思いますが、どっちの手間が上ですかね
ttyp03

2016/06/14 01:00

当然既存のコードを変更しないように手を入れることになりますが、既存コード+テストコードという構成になる以上、別物と言わざるを得ないと思います。考え方次第ではありますが。 手を入れた場合、既存のソースと相違ないということを客先に証明しないといけないという手間もありますし、例えば1000本あるソースに対してそれをやる手間を考えると、やはりこの案は(私の環境では)採用できません。
Zuishin

2016/06/14 01:04

了解しました。自社のソースを改良することもできない環境なのですね。大変でしょう。同情します。
ttyp03

2016/06/14 01:44

自社ではないですよ。なので尚更気を使います。
Zuishin

2016/06/14 01:50

どちらにしても大変ですね。ご自愛ください。 私はもう少し自由がきくもので、その感覚で言ってしまいました。
guest

0

「まったく既存のコードを編集せずに」ということであれば、おそらく不可能かと思います。

下請けになっている関数が外部結合を持つ場合(staticでない場合)、プログラム全体で同じ識別子を持った関数が2つ以上現れると、それは未定義の動作ということになります(むろん、コンパイラやリンカの指定でどうにかできる処理系が、ひょっとしたらあるかもしれませんが)。つまり、関数定義は1つだけしか書けなく、そのまま使う以外に選択肢はありません。

逆にstaticな関数の場合も、外部からはどうしようもありませんし、それどころかコンパイル時にインライン展開されて関数ごと雲散霧消しているかもしれません。

投稿2016/06/13 02:42

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttyp03

2016/06/13 04:35

同一プログラム内に同じ関数定義が存在できないのは承知です。 その一つの解として以前はマクロで置き換えるということで逃げたのですが、既存ソースを書き換えなくてもどうにかできたらと考えてます。 staticな関数については、テスト対象のソースをテストコードのソースにインクルードする方法で解決しました。 #include "test.c" void testcode() { test(); }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問