一个好的测试需要一个可控的环境,能够快速的重复的和可重用的。 为了创建一个测试用例,需要我们来被测代码(CUT)的任何输出。为了解决这些,我们需要打桩(Stub code),让上一层代码更容易使用,我们可以临时构建一段代码,测试隔离了原来的代码。
如何测试以下代码:
char *read_paragraph(int (*read)(void *), void *stream) {
int buffer_size = 0, length = 0;
char *buffer = NULL;
int ch;
while ((ch = (*read)(stream)) != EOF) {
if (++length > buffer_size) {
buffer_size += 100;
buffer = realloc(buffer, buffer_size + 1);
}
if ((buffer[length] = ch) == '/n') {
break;
}
}
return buffer;
}
这是个典型的返回c string,若果没有有内容返回NULL,para是从stream得来的,我们需要考虑到边界条件。
问题在于我们可以正确的得到stream,因为我们使用真正的stream,我们需要构造一个假的stream,让其返回NULL,我们需要写一个函数来模拟。
例如,我们需要stream为空的时候,我们需要 read_paragraph返回NULL 当stream读完的时候,即返回EOF。
static int empty_stream(void *stream) {
return EOF;
}
static void reading_lines_from_empty_stream_gives_null() {
assert_equal(read_paragraph(&empty_stream, NULL), NULL);
}
TestSuite *stream_tests()
TestSuite *suite = create_test_suite();
add_test(suite, reading_lines_from_empty_stream_gives_null);
return suite;
}
这个模拟很简单,因为只返回一个值。当测试结果要求返回值变化的时候就会变得复杂了,经常需要每个case都需要不同的返回值。我们不能都写不同的stub吧。
我们可以重复我们的代码 stub_stream()函数(任何名字都行)...
static int stub_stream(void *stream) {
return (int)stubbed_result();
}
这样我们只需要控制stubbed_result就可以了
假设我们让上述函数返回 EOF...
static int stub_stream(void *stream) {
return (int)stubbed_result();
}
static void reading_lines_from_empty_stream_gives_null() {
always_return(stub_stream, EOF);
assert_equal(read_paragraph(&stub_stream, NULL), NULL);
}
always_return() 宏得到函数名和返回值,能够让调用stub_stream() 时候返回EOF。返回结果通过assert_equal宏来验证。