单元测试期间的一些问题解决
最近在做单元测试的工作,原来也曾经坐过这样的工作,不过那时是自己写的函数自己测,所以对函数的逻辑和数据结构很了解,但是现在是读别人的代码,然后做单元测试,按理说做单元测试的话我应该把精力和重点放在本模块的函数上,对被测函数内部遇到的其他模块的函数应该不需要了解其处理细节,只要为其打桩调用一下就好了,可谁知偌大一个公司,这单元测试竟都是各个模块的developer各自组织本模块的测试框架,竟然没人知道怎么给函数打桩,这让我做起来相当的痛苦,但是面对问题还是要解决的,所以现在将自己遇到的问题解决过总结记录一下吧。
首先我知道曾经用过的gtest工具是开源的软件,并且支持函数打桩,所以我先搜集相关资料:
++ G Test
版本:1.5.0 *1) 如何运行测试* #define RUN_ALL_TESTS()\ (::testing::UnitTest::GetInstance()->Run()) ------------------------ UnitTestImpl::RunAllTests() test_cases_.ForEach(TestCase::RunTestCase) TestCase::RunTestCase static void RunTestCase(TestCase * test_case) { test_case->Run(); } TestCase::Run() test_info_list_->ForEach(internal::TestInfoImpl::RunTest); static void RunTest(TestInfo * test_info) { test_info->impl()->Run();} TestInfoImpl::Run() test = factory_->CreateTest() test->Run(); Test::Run() TestBody(); ------------------------ 1. UnitTest 单例,总管整个测试,包括测试环境信息,当前执行状态等等。 2. UnitTestImpl UnitTest内部具体功能的实现者。 3. Test 我们自己编写的,或通过TEST,TEST_F等宏展开后的Test对象,管理着测试案例的前后事件,具体的执行代码TestBody。 4. TestCase 测试案例对象,管理着基于TestCase的前后事件,管理内部多个TestInfo。 5. TestInfo 管理着测试案例的基本信息,包括Test对象的创建方法。 6. TestInfoImpl TestInfo内部具体功能的实现者 。 ---------------- *2 )如何生成测试* --------------- TEST(FooTest, Demo) { EXPECT_EQ(1, 1); } --------------- 展开 --------------- class FooTest_Demo_Test : public ::testing::Test { public: FooTest_Demo_Test() {} private: virtual void TestBody(); static ::testing::TestInfo* const test_info_; FooTest_Demo_Test(const FooTest_Demo_Test &); void operator=(const FooTest_Demo_Test &); }; ::testing::TestInfo* const FooTest_Demo_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "FooTest", "Demo", "", "", (::testing::internal::GetTestTypeId()), ::testing::Test::SetUpTestCase, ::testing::Test::TearDownTestCase, new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>); void FooTest_Demo_Test::TestBody() { switch (0) case 0: if (const ::testing::AssertionResult gtest_ar = (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1", "1", 1, 1))) ; else ::testing::internal::AssertHelper( ::testing::TPRT_NONFATAL_FAILURE, ".\\gtest_demo.cpp", 9, gtest_ar.failure_message() ) = ::testing::Message(); } 展开后,我们观察到: 1. TEST宏展开后,是一个继承自testing::Test的类。 2. 我们在TEST宏里面写的测试代码,其实是被放到了类的TestBody方法中。 3. 通过静态变量test_info_,调用MakeAndRegisterTestInfo对测试案例进行注册。 ++ Test Info 我们看到,上面创建了一个TestInfo对象,然后通过AddTestInfo注册了这个对象。TestInfo对象到底是一个什么样的东西呢? TestInfo对象主要用于包含如下信息: 1. 测试案例名称(testcase name) 2. 测试名称(test name) 3. 该案例是否需要执行 4. 执行案例时,用于创建Test对象的函数指针 5. 测试结果 我们还看到,TestInfo的构造函数中,非常重要的一个参数就是工厂对象,它主要负责在运行测试案例时创建出Test对象。我们看到我们上面的例子的factory为: new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test> 我们明白了,Test对象原来就是TEST宏展开后的那个类的对象(FooTest_Demo_Test),再看看TestFactoryImpl的实现: template <class TestClass> class TestFactoryImpl : public TestFactoryBase { public: virtual Test* CreateTest() { return new TestClass; } }; ++ Test Factory Impl ++ Make And Register Test Info *3)如何使用gtest* ++ How To Use Gtest |
Step1. 编译gtest-all.cc和gtest_main.cc文件
Step1. 编译gtest-all.cc和gtest_main.cc文件
g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest_main.cc
Step2. 将step1生成的gtest-all.o和gtest_main.o打包成静态库libgtest.a
ar -rv libgtest.a gtest-all.o gtest_main.o
Step3. 编译要测试的代码(假设文件名为sample.cpp)
g++ -I${GTEST_DIR}/include -c sample.cpp
Step4. 编译单元测试的代码(假设文件名为test.cpp)
g++ -I${GTEST_DIR}/include -c test.cpp
Step5. 与libgtest.a或其他需要的库链接、生成可执行程序
g++ -I${GTEST_DIR}/include test.o sample.o libgtest.a -o test
其他的库,如pthread库。
Where, GTEST_DIR=/usr/src/gtest-1.5.0
编写的makefile文件如下。
简单版本
all:
g++ -I/usr/src/gtest-1.5.0/include -I/usr/src/gtest-1.5.0 -g -c /usr/src/gtest-1.5.0/src/gtest-all.cc
g++ -I/usr/src/gtest-1.5.0/include -I/usr/src/gtest-1.5.0 -g -c /usr/src/gtest-1.5.0/src/gtest_main.cc
ar -rv libgtest.a gtest-all.o gtest_main.o
g++ -I/usr/src/gtest-1.5.0/include -g -c sample.cpp
g++ -I/usr/src/gtest-1.5.0/include -g -c test.cpp
g++ -I/usr/src/gtest-1.5.0/include -lpthread test.o sample.o libgtest.a -g -o test
clean:
rm test libgtest.a *.o
实际上,其中将gtest-all.o和gtest_main.o压缩为libgtest.a库,可以省去,直接使用.o文件,如下。
all:
g++ -I/usr/src/gtest-1.5.0/include -I/usr/src/gtest-1.5.0 -g -c /usr/src/gtest-1.5.0/src/gtest-all.cc
g++ -I/usr/src/gtest-1.5.0/include -I/usr/src/gtest-1.5.0 -g -c /usr/src/gtest-1.5.0/src/gtest_main.cc
g++ -I/usr/src/gtest-1.5.0/include -g -c sample.cpp
g++ -I/usr/src/gtest-1.5.0/include -g -c test.cpp
g++ -I/usr/src/gtest-1.5.0/include -lpthread test.o sample.o gtest-all.o gtest_main.o -g -o test
clean:
rm test *.o
正式版本
# Google Test directory
GTEST_DIR = /usr/src/gtest-1.5.0
# Flags passed to the preprocessor.
CPPFLAGS += -I$(GTEST_DIR)/include
# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra
# All Google Test headers. Usually you shouldn't change this definition.
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h $(GTEST_DIR)/include/gtest/internal/*.h
# All Google Test sources
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
# All tests produced by this Makefile. Remember to add new tests you created to the list.
TESTS = test
all : $(TESTS)
clean :
rm -f $(TESTS) gtest.a gtest_main.a *.o
gtest-all.o : $(GTEST_DIR)/src/gtest-all.cc # $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c $(GTEST_DIR)/src/gtest-all.cc
gtest_main.o : $(GTEST_DIR)/src/gtest_main.cc # $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c $(GTEST_DIR)/src/gtest_main.cc
gtest_main.a : gtest-all.o gtest_main.o
$(AR) $(ARFLAGS) $@ $^
sample.o : sample.cpp sample.h $(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c sample.cpp
test.o : test.cpp sample.h $(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c test.cpp
test : sample.o test.o gtest_main.a
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
$^代表依赖项,$@代表目标。
Reference
Readme
Makefile of sample
本篇文章来源于:开发学院 http://edu.codepub.com 原文链接:http://edu.codepub.com/2011/0116/28837.php