使用
ctest
编写单元测试。要测试的功能是打印输出到 stdout/stderr。
所以,现在,我有:
typedef struct {
char *stdout_file;
char *stderr_file;
} test_config;
static int group_setup(void **state) {
test_config *tc = malloc(sizeof(test_config));
tc->stdout_file = strdup("/tmp/new_stdout.txt");
tc->stderr_file = strdup("/tmp/new_stderr.txt");
*state = tc;
return 0;
}
static int setup(void **state) {
test_config *tc = *state;
if(!freopen(tc->stdout_file, "a+", stdout)) {
fprintf(stderr, "redirection failed to %s: %m", tc->stdout_file);
return -1;
}
if(!freopen(tc->stderr_file, "a+", stderr)) {
fprintf(stderr, "redirection failed to %s: %m", tc->stderr_file);
return -1;
}
return 0;
}
static int teardown(void **state) {
test_config *tc = *state;
/// How to restore stdout???
return 0;
}
static int group_teardown(void **state) {
test_config *tc = *state;
free(tc->stdout_file);
free(tc->stderr_file);
free(tc);
return 0;
}
static void test(void **state) {
function_to_test(); // it should always print something to stdout or stderr
char buffer[LINE_MAX];
FILE *out = fopen(tc->stdout_file, "r");
fread(buffer, 1, sizeof(buffer), out);
fclose(out);
int rc = memcmp(buffer, expected_output, strlen(expected_output));
assert_int_equal(rc, 0);
}
int main() {
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test, setup, teardown),
}
return cmocka_run_group_tests(tests, group_setup, group_teardown);
}
我遇到的问题是用于重定向的文件未创建。但不会打印来自
freopen()
失败的消息。在 LastTest.log 中我看到:
[ RUN ] mytest
<end of output>
这让我相信重定向确实发生了。但文件在哪里?
如何将 stdout/stderr 返回到
ctest
?为什么我在测试日志中看不到[ PASSED ] mytest
?
有更好的方法吗?
ctest
不应该已经有一些内置的功能来检查标准输出的内容吗?这是相当普遍的问题,不是吗?
最后我是这样解决这个问题的:
static int setup(void **state) {
test_config *tc = *state;
// nothing to do with capturing TTY
return 0;
}
static int teardown(void **state) {
test_config *tc = *state;
unlink(tc->stdout_file);
unlink(tc->stderr_file);
return 0;
}
static void capture_tty(void **state) {
test_config *tc = *state;
fflush(stdout);
tc->stdout_fd = dup(STDOUT_FILENO);
int redir_fd = open(tc->stdout_file, O_WRONLY | O_CREAT, S_IREAD | S_IWRITE);
dup2(redir_fd, STDOUT_FILENO);
close(redir_fd);
// same for stderr
}
static void release_tty(void **state) {
test_config *tc = *state;
fflush(stdout);
dup2(tc->stdout_fd, STDOUT_FILENO);
close(tc->stdout_fd);
// same for stderr
}
static void test(void **state) {
capture_tty(state);
function_to_test(); // it should always print something to stdout or stderr
release_tty(state);
// at this moment, standard streams is back under ctest's control and
// all assert_*() work as expected.
}
该方法可以完整捕获刚刚测试的函数的 stdout/stderr。同时不干扰
ctest
自己的捕捉。
谢谢各位的指点。