gcc 4.7.2
c89
こんにちは、
私のCプログラムの簡単な単体テストを作成するためにmakefileを使用することをカバーするチュートリアルやテキストブックを知っている人はいますか?
テスト スイートを作成し、これを Makefile に追加する自動テストを実行したいと考えています。
始める方法についていくつかのアイデアが欲しいだけです。
提案に感謝します
gcc 4.7.2
c89
こんにちは、
私のCプログラムの簡単な単体テストを作成するためにmakefileを使用することをカバーするチュートリアルやテキストブックを知っている人はいますか?
テスト スイートを作成し、これを Makefile に追加する自動テストを実行したいと考えています。
始める方法についていくつかのアイデアが欲しいだけです。
提案に感謝します
はい、確かに、30行未満の makefileで、汎用の単体テストエンジンを構築できます。
gawkとlispスクリプトをテストするために次のように書いたが、 c用に簡単にカスタマイズできることに注意してください。実際、私見では、すべてがシェルスクリプトの力の良い例です。
まず、すべてのテストを実行可能ファイルとしていくつかのに配置します$Testdir
。この例では、すべてのテストにファイル名001
002
など(拡張子なし)があります。
次に、いくつかのセットアップが必要です。
Here=$(PWD)
Testdir=$(Here)/eg
ready: $(Testdir) $(Tmp)
$(Tmp) :
@ - [ ! -d $(Tmp) ] && mkdir $(Tmp)
次に、すべてのテストをというリストにまとめる必要があります$Tests
Tests:=$(shell cd $(Testdir); ls | egrep '^[0-9]+$$' | sort -n )
(の使用に注意してください。これは、一度ビルドして何度も使用:=
するわずかな最適化です。)$Tests
$(X)
私のリストの各ファイルは、 $Tests
2つの方法で実行できます。まず、あなたはrun
それだけができます。次に、それを実行しcache
て結果をで実行できます$(X).want
。
run : ready $(Testdir)/$(X)
@echo $X 2>&1
@cat $(Testdir)/$(X) | $(Run)
cache : ready
@$(MAKE) run | tee $(Testdir)/$X.want
@echo new test result cached to $(Testdir)/$X.want
テストの準備が整い、適切な出力が生成されたらcache
、テストの結果を確認します。
実際の実行は、と呼ばれる魔法の変数によって定義されます$(Run)
。これは、テスト対象の言語用に特別に作成する必要があるものです。ちなみに、私はGawkスクリプトをテストしているので、$(Run)
次のようになります(必要に応じて変更できます)。
Run= gawk -f mycode.awk
それが完了したら、1つのテストを実行するために、実行後に取得したものを$(X)
キャッシュされたコピーと比較します。
test : ready $(Testdir)/$(X).want
@$(MAKE) run > $(Tmp)/$X.got
@if diff -s $(Tmp)/$X.got $(Testdir)/$X.want > /dev/null; \
then echo PASSED $X ; \
else echo FAILED $X, got $(Tmp)/$X.got; \
fi
これが私がすべてのテストを実行する方法です:
tests:; @$(foreach x, $(Tests), $(MAKE) X=$x test;)
現在のすべての出力のバッチキャッシュを実行することもできます(警告:テストが現在正しい出力を生成している場合を除いて、これを実行しないでください)。
cache :
@$(foreach x, $(Tests), $(MAKE) X=$x cache;)
最後に、必要に応じて、合格と不合格の最終スコアを取得することもできます。
score :
@$(MAKE) tests | cut -d\ -f 1 | egrep '(PASSED|FAILED)' | sort | uniq -c
それだけです:約束通り、Makeinの汎用ユニットツールは30行未満です。共有してお楽しみください。
gccxml
とを使用すると、関数スニペットを簡単に生成できますnm
。次の bash スクリプトgenerate_snippets.sh
を 1 つのコマンド ライン引数で呼び出して、ソース ファイルで定義された各関数のテスト関数スニペットを生成できます。
#!/bin/bash
#
# Generate stub functions for one file
#
# # Initialize
FILE=$1
[ ! -e "$FILE" ] && echo "file doesn't exist" && exit -1
# # Compile
OBJECT=$(mktemp)
gcc -c $FILE -o $OBJECT
# # Get Functions
# ## Get all symbols in the text sections
Y=$(mktemp)
nm $OBJECT | grep " T " | awk '{print $3;}' | sort > $Y
# ## Get functions defined in the file (including #includes)
# get all functions defined in the compilation unit of $FILE, excluding included
# dynamically linked functions
XML=$(mktemp)
X=$(mktemp)
gccxml $FILE -fxml=$XML
grep "<Function" $XML | sed 's/^.*name="\([^"]*\).*/\1/g' | sort > $X
# ## get the common lines
# This is done to get those functions which are defined in the source file and end up in
# the compiled object file.
COMMON=$(comm $Y $X -1 -2)
# # Create stubs
for func in $COMMON;
do
cat <<_
// Test stub for $func. Returns 1 if it fails.
char test_$func() {
return 1;
}
_
done
# # Clean up
rm $OBJECT $XML $X $Y
スクリプトはまだ完全ではありません。おそらく、まだテストされていない関数のテスト関数のみを生成するテストを含める必要があります。これは と の間の一般的な名前を見つけることに類似しているため、$X
これ$Y
は演習として残します。これが実装されている場合、このスクリプトを Makefile から実行することは理にかなっています。その点については、他の回答を参照してください。
Cファイルを考えてみましょうhello.c
:
#include <stdio.h>
int foo();
int bar();
int main() {
printf("hello, world\n");
return 0;
}
int foo() {
printf("nothing\n");
return 1;
}
int bar() {
printf("still nothing\n");
return 1;
}
このファイルを入力として上記のスクリプトを実行すると、次の出力が生成されます。
// Test stub for bar. Returns 1 if it fails.
char test_bar() {
return 1;
}
// Test stub for foo. Returns 1 if it fails.
char test_foo() {
return 1;
}
// Test stub for main. Returns 1 if it fails.
char test_main() {
return 1;
}
これらのスニペットを適切なファイルに入れ、必要に応じてロジックを入力してください。その後、テスト スイートをコンパイルして実行します。