场景:
1. 单元测试在开发中保证程序质量是必修课,快速编写单元测试是能节约不少时间。
2. C++的单元测试框架gtest是测试的首选,简单,轻量,快速。
3. 开发时,对项目源码进行测试需要新建分离的单元测试项目,以下就是用python脚本直接创建单元测试项目的模板,模板里包括常用的单元测试断言和辅助类,这个是使用Makefile做编译配置的,如果需要生成vs的项目,建议使用google的gyp.
说明: 运行Init.py会在当前文件夹生成模板Makefile文件,include目录生成main.h文件,src目录生成main.cpp和test_xx.cpp模版测试文件.在windows上,我用的MinGW的gcc编译器和msys的makefile.
文件 Init.py
#! encoding=utf-8
import re
import os
import sys
kMakefile = '''
#Makefile template
OBJECTDIR=build/Debug
objects = \\
${OBJECTDIR}/src/main.o \\
${OBJECTDIR}/src/test_xx.o
# CC Compiler Flags
CXXFLAGS=-m32 -g -Wall -MMD -MP -MF $@.d
CPPFLAGS=-Iinclude \\
-I../../include \\
-I/E/software/Lib/tests/gtest-1.5.0/win32/release/static/include-compile \\
-I/E/software/Lib/tests/gtest-1.5.0/win32/release/static/include
# Link Libraries and Options
LDLIBSOPTIONS=-L/E/software/Lib/tests/gtest-1.5.0/win32/release/static -lgtest \\
-static-libgcc
all: ${objects}
mkdir -p dist
${CXX} -m32 -o dist/app ${objects} ${LDLIBSOPTIONS}
${objects}: ${OBJECTDIR}/%.o: %.cpp
mkdir -p $(dir $@)
rm -fr $@.d
$(COMPILE.cc) $< -o $@
clean:
rm -fr build
rm -fr dist
'''
kMainHeader = '''
#ifndef MAIN_H
#define MAIN_H
extern int kArgc;
extern char** kArgv;
#endif /* MAIN_H */
'''
kMainSrc = '''
#include "main.h"
#include <stdio.h>
#include <assert.h>
#include <string>
#include "gtest/gtest.h"
int kArgc;
char** kArgv;
int main(int argc, char **argv)
{
setbuf(stdout,(char*)0);
setbuf(stderr,(char*)0);
kArgc = argc;
kArgv = argv;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
// 在 googletest 中实现单元测试,可通过 ASSERT_* 和 EXPECT_*
// 断言来对程序运行结果进行检查。 ASSERT_* 版本的断言失败时会产生致命失败,
// 并结束当前函数; EXPECT_* 版本的断言失败时产生非致命失败,但不会中止当前函数。
// 因此, ASSERT_* 常常被用于后续测试逻辑强制依赖的处理结果的断言,
// 如创建对象后检查指针是否为空,若为空,则后续对象方法调用会失败;
// 而 EXPECT_* 则用于即使失败也不会影响后续测试逻辑的处理结果的断言,
// 如某个方法返回结果的多个属性的检查。
TEST(test_main,TestBaseAssert)
{
ASSERT_TRUE(__LINE__);
EXPECT_TRUE(__LINE__);
ASSERT_FALSE(false);
EXPECT_FALSE(false);
}
TEST(test_main,TestBinaryAssert)
{
const char* str = "11";
ASSERT_EQ(str,str);
EXPECT_EQ(false,0);
ASSERT_NE(str,str+1);
EXPECT_NE(false,1);
ASSERT_LT(str,str+1);
EXPECT_LT(0x1,0x2);
ASSERT_LE(0x1,0x2);
EXPECT_LE(0x1,0x2);
ASSERT_GT(0x2,0x1);
EXPECT_GT(0x2,0x1);
ASSERT_GE(0x2,0x1);
EXPECT_GE(0x2,0x1);
}
TEST(test_main,TestStrAssert)
{
const char* str = "11ab";
std::string str2("11ab");
std::string str21("11aB");
ASSERT_STREQ(str,str2.c_str());
EXPECT_STREQ(str,str2.c_str());
ASSERT_STRNE(str,str21.c_str());
EXPECT_STRNE(str,str21.c_str());
ASSERT_STRCASEEQ(str,str21.c_str());
EXPECT_STRCASEEQ(str,str21.c_str());
str21.append("!");
ASSERT_STRCASENE(str,str21.c_str());
EXPECT_STRCASENE(str,str21.c_str());
}
'''
kTestXXSrc = '''
#include "main.h"
#include <stdio.h>
#include <assert.h>
#include <string>
#include "gtest/gtest.h"
#include "gtest/internal/gtest-filepath.h"
#include "gtest/internal/gtest-port.h"
using std::string;
using ::testing::internal::FilePath;
using ::testing::Message;
TEST(test_xx,XX)
{
FilePath path(kArgv[0]);
FilePath currentDir = path.GetCurrentDir();
Message message("currentDir is: ");
message << currentDir.ToString();
GTEST_LOG_(INFO) << message ;
GTEST_LOG_(INFO) << ".............." ;
}
'''
if __name__=='__main__':
# makefile
global kMakefile
global kMainHeader
global kMainSrc
global kTestXXSrc
file = open("Makefile","w")
file.write(kMakefile)
file.close()
# dirs
try:
os.mkdir("include")
os.mkdir("src")
except Exception, e:
pass
# sample code
file = open("./src/main.cpp","w")
file.write(kMainSrc)
file.close()
file = open("./src/test_xx.cpp","w")
file.write(kTestXXSrc)
file.close()
file = open("./include/main.h","w")
file.write(kMainHeader)
file.close()
运行模版代码的单元测试输出: