I am on my way

导航

单元测试期间的一些问题解决

    最近在做单元测试的工作,原来也曾经坐过这样的工作,不过那时是自己写的函数自己测,所以对函数的逻辑和数据结构很了解,但是现在是读别人的代码,然后做单元测试,按理说做单元测试的话我应该把精力和重点放在本模块的函数上,对被测函数内部遇到的其他模块的函数应该不需要了解其处理细节,只要为其打桩调用一下就好了,可谁知偌大一个公司,这单元测试竟都是各个模块的developer各自组织本模块的测试框架,竟然没人知道怎么给函数打桩,这让我做起来相当的痛苦,但是面对问题还是要解决的,所以现在将自己遇到的问题解决过总结记录一下吧。

    首先我知道曾经用过的gtest工具是开源的软件,并且支持函数打桩,所以我先搜集相关资料:

   

gtest剖析(草稿)
2010-12-24 00:57
++ 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

template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
    virtual Test* CreateTest() { return new TestClass; }
};

 ++ Make And Register Test Info

// 创建一个 TestInfo 对象并注册到 Google Test;
// 返回创建的TestInfo对象
//
// 参数:
//
//   test_case_name:            测试案例的名称
//   name:                           测试的名称
//   test_case_comment:       测试案例的注释信息
//   comment:                      测试的注释信息
//   fixture_class_id:             test fixture类的ID
//   set_up_tc:                    事件函数SetUpTestCases的函数地址
//   tear_down_tc:               事件函数TearDownTestCases的函数地址
//   factory:                        工厂对象,用于创建测试对象(Test)
TestInfo* MakeAndRegisterTestInfo(
    const char* test_case_name, const char* name,
    const char* test_case_comment, const char* comment,
    TypeId fixture_class_id,
    SetUpTestCaseFunc set_up_tc,
    TearDownTestCaseFunc tear_down_tc,
    TestFactoryBase* factory) {
  TestInfo* const test_info =
      new TestInfo(test_case_name, name, test_case_comment, comment,
                   fixture_class_id, factory);
  GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
  return test_info;
}

--------------------------

*3)如何使用gtest*

++ How To Use Gtest

gtest使用简介

最近测试过程中使用gtest,简单易用,是一个非常不错的单元测试框架,简单介绍下使用方法,欢迎大家拍砖
一、安装
官网下载地址:http://code.google.com/p/googletest/
1、下载最新版本:gtest-1.4.0.tar.gz
2、解压后进入gtest-1.4.0
3、  运行./configure  make  make install(需要sudo权限,也可以自己指定安装的目录)
4、  进入根目录,编辑.bashrc,添加库文件路径export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
(编译测试程序时可以找到生成的gtest库文件)
 
二、调用
1、编写测试程序时添加#include <gtest/gtest.h>
2、编译测试程序时添加以下内容-ldl -lgtest -I/usr/local/include(加载gtest头文件)
 
代码:

#include <iostream>

#include <cstdlib>

using namespace std;


#include <gtest/gtest.h>


int Fun(int val)

{

 return val;

}


//测试用例

TEST(Test, TestFun)

{

 //检查函数的返回值是否与给定的值相等

 EXPECT_EQ(10, Fun(10));

 EXPECT_EQ(9, Fun(10));

}


int main(int argc, char* argv[])

{

 //初始化

 testing::InitGoogleTest(&argc, argv);

 //运行所有的测试用例

 RUN_ALL_TESTS();

 system("pause");

 return 0;

}


宏与事件

 断言宏可以理解分为两类,一类是ASSERT,另外一类是EXPECT系列。当检查点失败的时候,ASSERT系列的宏会退出当前函数,EXPECT系列的断言会继续往下执行。

 Gtest中定义了多种宏来针对不同的需求,有针对布尔类型的宏,有针对数值类型进行逻辑检查的宏,针对字符串的宏。针对异常检查的宏,还有一些针对特殊情况下使用的宏。

宏的主要作用就是检查所有测试的函数是否按照我们的预期进行了工作。


事件机制:使我们能在案例之前和之后做一些操作。事件一般分为三种类型:

1. 全局的,所有案例执行前后。

2. TestSuit级别的,在某一批案例中的第一个案例前,最后一个案例执行后。

3. TestCase级别的,每一个TestCase前后。

事件的实现

4. 全局事件 写一个类,继承test::Environment类,实现里面的setup和teardown。Setup在所有案例执行前执行,teardown在所有案例执行后执行。

5. TestSuit 写一个类继承testing::Test,然后实现两个静态方法,setuptestcase,它在第一个testcase之前执行。Teardowntestcase在最后一个testcase之后执行。然后再编写测试案例的时候,需要使用TEST_F宏,第一个参数使用,我们派生类的名字。

6. TestCase 写一个类,继承testing::Test,然后重写方法setup和teardown。Setup方法在每个testcase之前执行,teardown方法在每个testcase之后执行。编写测试案例的时候同样需要使用TEST_F宏。
7.
四、程序部分测试结果
 
Gtest对每个失败的case给出预期值与实际值的差别以及失败case的名称
 
五、gtest断言
布尔值检查
Fatal assertion
Nonfatal assertion
Verifies
ASSERT_TRUE(condition);
EXPECT_TRUE(condition);
conditionis true
ASSERT_FALSE(condition);
EXPECT_FALSE(condition);
conditionis false
数值型数据检查

Fatal assertion
Nonfatal assertion
Verifies
ASSERT_EQ(expected, actual);
EXPECT_EQ(expected, actual);
expected == actual
ASSERT_NE(val1, val2);
EXPECT_NE(val1, val2);
val1 != val2
ASSERT_LT(val1, val2);
EXPECT_LT(val1, val2);
val1 < val2
ASSERT_LE(val1, val2);
EXPECT_LE(val1, val2);
val1 <= val2
ASSERT_GT(val1, val2);
EXPECT_GT(val1, val2);
val1 > val2
ASSERT_GE(val1, val2);
EXPECT_GE(val1, val2);
val1 >= val2
字符串检查

Fatal assertion
Nonfatal assertion
Verifies
ASSERT_STREQ(expected_str, actual_str);
EXPECT_STREQ(expected_str, actual_str);
the two C strings have the same content
ASSERT_STRNE(str1, str2);
EXPECT_STRNE(str1, str2);
the two C strings have different content
ASSERT_STRCASEEQ(expected_str, actual_str);
EXPECT_STRCASEEQ(expected_str, actual_str);
the two C strings have the same content, ignoring case
ASSERT_STRCASENE(str1, str2);
EXPECT_STRCASENE(str1, str2);
the two C strings have different content, ignoring case
     更多详细资料请参看
官方文档:http://code.google.com/p/googletest/wiki/GoogleTestPrimer
推荐文档:http://www.cnblogs.com/coderzh/archive/2009/03/31/1426758.html

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


posted on 2011-03-04 09:42  Haidi  阅读(957)  评论(1编辑  收藏  举报