gcc 4.7.2
c89
你好,
我想知道是否有人知道任何涵盖使用makefile为我的c程序创建一些简单单元测试的教程或教科书。
我想运行一些自动化测试来创建一个测试套件并将其添加到我的 Makefile 中。
只是想要一些关于如何开始的想法。
非常感谢您的任何建议
gcc 4.7.2
c89
你好,
我想知道是否有人知道任何涵盖使用makefile为我的c程序创建一些简单单元测试的教程或教科书。
我想运行一些自动化测试来创建一个测试套件并将其添加到我的 Makefile 中。
只是想要一些关于如何开始的想法。
非常感谢您的任何建议
确实是的,不到 30 行的 makefile就可以构建一个通用的单元测试引擎。
请注意,我为测试gawk和lisp脚本编写了以下内容,但可以轻松地为c定制它。实际上,恕我直言,整个事情都是shell脚本功能的一个很好的例子。
首先,您将所有测试都是可执行文件放在一些$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 )
(注意使用:=
. The 是一个轻微的优化,构建$Tests
一次,多次使用。)
$(X)
我列表中的每个文件$Tests
都可以通过两种方式执行。首先,你可以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
一旦完成,然后运行一个测试,我只需将运行后得到$(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;)
最后,如果您愿意,您还可以获得 PASSED 和 FAILED 的最终分数:
score :
@$(MAKE) tests | cut -d\ -f 1 | egrep '(PASSED|FAILED)' | sort | uniq -c
就是这样:正如所承诺的 - Make 中的通用单元工具,不到 30 行。分享和享受。
gccxml
在和的帮助下生成函数片段很容易nm
。generate_snippets.sh
可以使用一个命令行参数调用以下 bash 脚本,为源文件中定义的每个函数生成一个测试函数片段:
#!/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;
}
只需将这些片段放入适当的文件中,并根据需要用逻辑填充它们。之后,编译测试套件并运行它。