CppTest安装与使用

 安装:

下载cpptest-1.1.1.tar.gz

解压:tar -zxvf cpptest-1.1.1.tar.gz

进入解压后的目录

1.         ./configure

2.         make

3.         make install

 

什么是测试套件?

单元测试用于测试源代码的特定部分。形式最简单的测试套件包含一组测试其他 C/C++ 代码的 C/C++ 函数。CppTest 在 Test 名称空间中定义一个名为 Suite 的类,它提供基本的测试套件功能。用户定义的测试套件必须扩展这个功能,定义作为实际单元测试运行的函数。清单 1 定义一个名为 myTest 的类,其中有两个函数,分别测试一段源代码。TEST_ADD 是用于注册测试的宏。


清单 1. 扩展基本的 Test::Suite 类

#include “cpptest.h”

 

class myTest : public Test::Suite {

  void function1_to_test_some_code( );

  void function2_to_test_some_code( );

 

  myTest( ) {

      TEST_ADD (myTest::function1_to_test_some_code);

      TEST_ADD (myTest::function2_to_test_some_code);

  }

};

 

扩展测试套件

您可以很容易扩展测试套件的功能,创建测试套件的层次结构。每个测试套件可能测试不同的代码部分,例如分别测试编译器的解析、代码生成和代码优化,创建层次结构有助于更好地管理。清单 2 演示如何创建这样的层次结构。


清单 2. 创建测试套件层次结构

#include “cpptest.h”

 

class unitTestSuite1 : public Test::Suite { … }

class unitTestSuite2 : public Test::Suite { … }

 

class myTest : public Test::Suite {

  myTest( ) {

      add (std::auto_ptr<Test::Suite>(new unitTestSuite1( )));

      add (std::auto_ptr<Test::Suite>(new unitTestSuite2( )));

  }

};

 

add 方法属于 Suite 类。清单 3 给出它的原型(取自头文件 cpptest-suite.h)。

 

清单 3. Suite::add 方法声明

class Suite

{

 public:   

    …

    void add(std::auto_ptr<Suite> suite);

    ...

} ;

 

运行第一个测试

Suite 类的 run 方法负责执行测试。清单 4 演示如何运行测试。

清单 4. 以详细模式运行测试套件

#include “cpptest.h”

 

class myTest : public Test::Suite {

  void function1_to_test_some_code( ) { … };

  void function2_to_test_some_code( ) { … };

 

  myTest( ) {

      TEST_ADD (myTest::function1_to_test_some_code);

      TEST_ADD (myTest::function2_to_test_some_code);

  }

};

 

int main ( )

{

  myTest tests;

  Test::TextOutput output(Test::TextOutput::Verbose);

  return tests.run(output);

}

 

run 方法返回一个 Boolean 值,只有所有单元测试都成功,这个值才会设置为 True。run 方法的参数是一个 TextOutput 类型的对象。TextOutput 类处理测试日志的输出。在默认情况下,日志输出到屏幕。

除了详细模式,还有简洁模式。这两种模式的差异是,详细模式输出测试中失败的断言的行号/文件名信息,而简洁模式只提供成功或失败的测试数量。

失败后继续执行

那么,如果一个测试失败了,会怎么样?是否继续执行测试由客户机代码决定。默认行为是继续执行其他测试。清单 5 给出 run 方法的原型。


清单 5. run 方法的原型

                                    

bool Test::Suite::run(      

    Output &   output,

    bool                 cont_after_fail = true     

);

 

如果在探测到第一个失败之后回归测试必须退出,那么 run 方法的第二个参数应该是 False。但是,可能不容易判断什么时候应该把第二个标志设置为 False。假设客户机代码试图向一个已经完全满了的磁盘写信息:代码将失败,套件中行为相似的所有其他测试也会失败。在这种情况下,立即停止回归测试是合理的。

 

输出格式化器

输出格式化器背后的思想是,可能需要不同格式的回归测试运行报告:文本、HTML 等等。因此,run 方法本身并不转储结果,而是接受一个 Output 类型的对象,它负责显示结果。在 CppTest 中可以使用三种输出格式化器:

Test::TextOutput。这是所有输出处理器中最简单的一种。显示模式可以是详细或简洁。

Test::CompilerOutput。按照与编译器构建日志相似的方式生成输出。

Test::HtmlOutput。生成 HTML 输出。

在默认情况下,这三种格式化器都把输出发送到 std::cout。前两种格式化器的构造器接受一个 std::ostream 类型的参数,它指定输出的目标,例如可以把输出转储到文件中以便进一步分析。还可以创建输出格式化器的定制版本。为此,只需从 Test::Output 派生出用户定义的格式化器。可以通过清单 6 中的代码理解不同的输出格式。


清单 6. 以简洁模式运行 TEST_FAIL 宏

#include “cpptest.h”

 

class failTest1 : public Test::Suite {

void always_fail( ) {

  TEST_FAIL (“This always fails!\n”);

}

public:

  failTest1( ) { TEST_ADD(failTest1::always_fail); }

};

 

int main ( ) {

  failTest1 test1;

  Test::TextOutput output(Test::TextOutput::Terse);

  return test1.run(output) ? 1 : 0;

}

 

注意,TEST_FAIL 是 cppTest.h 中预定义的宏,它会导致断言失败。(稍后进一步讨论。)清单 7 显示输出。


清单 7. 简洁输出只显示失败数

failTest1: 1/1, 0% correct in 0.000000 seconds

Total: 1 tests, 0% correct in 0.000000 seconds

 

清单 8 显示以详细模式运行相同代码时的输出。


清单 8. 详细输出显示文件/行信息、消息、测试套件信息等等

failTest1: 1/1, 0% correct in 0.000000 seconds

        Test:    always_fail

        Suite:   failTest1

        File:     /home/arpan/test/mytest.cpp

        Line:    5

        Message: "This always fails!\n"

 

Total: 1 tests, 0% correct in 0.000000 seconds

 

使用编译器式格式的代码见清单 9。


清单 9. 用编译器式输出格式运行 TEST_FAIL 宏

#include “cpptest.h”

 

class failTest1 : public Test::Suite {

void always_fail( ) {

  TEST_FAIL (“This always fails!\n”);

}

public:

  failTest1( ) { TEST_ADD(failTest1::always_fail); }

};

 

int main ( ) {

  failTest1 test1;

  Test::CompilerOutput output;

  return test1.run(output) ? 1 : 0;

}

 

注意,输出格式与 GNU Compiler Collection (GCC) 生成的编译日志相似,见清单 10。


清单 10. GCC 风格的编译器输出

                                    

/home/arpan/test/mytest.cpp:5: “This always fails!\n”

 

在默认情况下,编译器格式的输出是 GCC 风格的构建日志。但是,也可以让输出采用 Microsoft® Visual C++® 和 Borland 编译器格式。清单 11 生成 Visual C++ 风格的日志并把日志转储到输出文件中。


清单 11. 用编译器式输出格式运行 TEST_FAIL 宏

#include <ostream>

 

int main ( ) {

  failTest1 test1;

  std::ofstream ofile;

  ofile.open("test.log");

  Test::CompilerOutput output(

      Test::CompilerOutput::MSVC, ofile);

 return test1.run(output) ? 1 : 0;

}

 

清单 12 显示执行清单 11 中的代码之后 test.log 文件的内容。


清单 12. Virtual C++ 风格的编译器输出

/home/arpan/test/mytest.cpp (5) :  “This always fails!\n”

 

最后是最有意思的:使用 HtmlOutput 的代码。注意,HTML 格式化器并不在构造器中接受文件句柄,而是依靠 generate 方法。generate 方法的第一个参数是一个 std::ostream 类型的对象,它的默认值是 std::cout(见源代码头文件 cpptest-htmloutput.h)。可以使用文件句柄把日志重定向到其他地方。清单 13 提供一个示例。


清单 13. HTML 风格的格式

#include *<ostream>

 

int main ( ) {

  failTest1 test1;

  std::ofstream ofile;

  ofile.open("test.log");

  Test::HtmlOutput output( );

  test1.run(output);

  output.generate(ofile);

  return 0;

}

 

清单 14 显示在 test.log 中生成的部分 HTML 输出。


清单 14. 生成的 HTML 输出的片段

<table summary="Test Failure" class="table_result">

  <tr>

    <td style="width:15%" class="tablecell_title">Test</td>

    <td class="tablecell_success">failTest1::always_fail</td>

  </tr>

  <tr>

    <td style="width:15%" class="tablecell_title">File</td>

    <td class="tablecell_success">/home/arpan/test/mytest.cpp:18</td>

  </tr>

  <tr>

    <td style="width:15%" class="tablecell_title">Message</td>

    <td class="tablecell_success">"This always fails!\n"</td>

  </tr>

</table>

测试装备

同一测试套件中的单元测试常常有相同的初始化需求:必须用某些参数创建对象、打开文件句柄/操作系统端口等等。不需要在每个类方法中重复相同的代码,更好的方法是使用一些共用的初始化和退出例程,让每个测试都调用它们。必须在测试套件中定义设置和退出方法。清单 15 定义测试套件 myTestWithFixtures,它使用测试装备。


清单 15. 创建包含装备的测试套件

#include “cpptest.h”

 

class myTestWithFixtures : public Test::Suite {

  void function1_to_test_some_code( );

  void function2_to_test_some_code( );

 

  public:

  myTest( ) {

      TEST_ADD (function1_to_test_some_code);

      TEST_ADD (function2_to_test_some_code);

  }

 

  protected:

    virtual void setup( ) { … };

    virtual void tear_down( ) { … };

};

 

注意,不需要显式地调用设置和退出方法。这些例程不一定要声明为虚拟的,除非打算以后扩展测试套件。这两个例程的返回类型必须是 void 而且不接受参数。

 

CppTest 中的宏

CppTest 提供几个有用的宏,可以在测试客户机源代码的实际方法中使用它们。这些宏在 cpptest-assert.h 中定义,cpptest.h 包含这个头文件。下面讨论一些宏和可能的用例。注意,除非另外声明,下面提供的输出都使用详细模式。

TEST_FAIL (message)

这个宏(见清单 16)无条件地产生失败。可以使用这个宏的典型情况是处理客户机函数的结果。如果结果不符合任何期望的结果,那么抛出一个包含消息的异常。当遇到 TEST_FAIL 宏时,不再执行单元测试中的其他代码。


清单 16. 使用 TEST_FAIL 宏的客户机代码

void myTestSuite::unitTest1 ( ) {

    int result = usercode( );

    switch (result) {

       case 0: // Do Something

       case 1: // Do Something

       …

       default: TEST_FAIL (“Invalid result\n”);

   }

}


TEST_ASSERT (expression)

这个宏与 C 断言库例程相似,但是 TEST_ASSERT 可以在调试和版本构建时起作用。如果表达式的结果是 False,就指出一个错误。清单 17 给出这个宏的内部实现。


清单 17. TEST_ASSERT 宏实现

#define TEST_ASSERT(expr)  \

{     \

  if (!(expr))  \

  { \

  assertment(::Test::Source(__FILE__, __LINE__, #expr));  \

     if (!continue_after_failure()) return;  \

  } \

}



TEST_ASSERT_MSG (expression, message)

这个宏与 TEST_ASSERT 相似,只是在断言失败时在输出中显示消息而不是表达式。下面是包含和不包含消息的宏示例。

TEST_ASSERT (1 + 1 == 0);

TEST_ASSERT (1 + 1 == 0, “Invalid expression”);

 

清单 18 显示遇到这两个宏时的输出。


清单 18. TEST_ASSERT 和 TEST_ASSERT_MSG 宏的输出

Test:    compare

Suite:   CompareTestSuite

File:     /home/arpan/test/mytest.cpp

Line:    91

Message: 1 + 1 == 0

 

Test:    compare

Suite:   CompareTestSuite

File:     /home/arpan/test/mytest.cpp

Line:    92

Message: Invalid Expression



TEST_ASSERT_DELTA (expression1, expression2, delta)

如果 expression1 和 expression2 的差超过了 delta,就抛出异常。这个宏在 expression1 和 expression2 是浮点数的情况下尤其有用;例如,根据实际采用的舍入方法不同,4.3 可能存储为 4.299999 或 4.300001,因此在进行比较时需要 delta。另一个示例是测试操作系统 I/O 的代码:打开文件花费的时间不可能每次都相同,但是必须在一定的范围内。

TEST_ASSERT_DELTA_MSG (expression, message)

这个宏与 TEST_ASSERT_DELTA 宏相似,只是在断言失败时还显示消息。

TEST_THROWS (expression, exception)

这个宏检验表达式并期望遇到某种异常。如果没有捕捉到异常,就触发断言。注意,对表达式的实际值并不进行任何测试;要测试的是异常。请考虑清单 19 中的代码。


清单 19. 处理整数异常

class myTest1 : public Test::Suite {

  …

  void func1( ) {

     TEST_THROWS (userCode( ), int);

  }

  public:

     myTest1( ) { TEST_ADD(myTest1::func1); }

};

 

void userCode( ) throws(int) {

   …

   throw int;

}

 

注意,userCode 例程的返回类型并不重要:它也可以是双精度数或整数。因为这里的 userCode 无条件地抛出 int 类型的异常,所以这个测试会顺利通过。

TEST_THROWS_ANYTHING (expression)

有时候,客户机例程根据具体情况抛出不同类型的异常。可以使用 TEST_THROWS_ANYTHING 宏处理这种情况,这个宏不需要指定期望的异常类型。只要在执行客户机代码之后捕捉到异常,就不会触发断言。

TEST_THROWS_MSG (expression, exception, message)

这个宏与 TEST_THROWS 相似,只是它输出消息而不是表达式。请考虑以下代码:

TEST_THROWS(userCode( ), int);

TEST_THROWS(userCode( ), int, “No expected exception of type int”);

 

清单 20 显示这些断言失败时的输出。


清单 20. TEST_THROWS 和 TEST_THROWS_MSG 宏的输出

Test:    func1

Suite:   myTest1

File:    /home/arpan/test/mytest.cpp

Line:    24

Message: userCode()

 

Test:    func2

Suite:   myTest1

File:    /home/arpan/test/mytest.cpp

Line:    32

Message: No expected exception of type int

 

 

 

//输出HTML形式的日志

#include <cstdlib>

#include <cstring>

#include <iostream>

#include <fstream>

 

#include "cpptest.h"

 

using namespace std;

 

class ExampleTestSuite : public Test::Suite

{

public:

    ExampleTestSuite()

    {

        TEST_ADD(ExampleTestSuite::first_test) //把测试函数加入测试套

        TEST_ADD(ExampleTestSuite::second_test)

    }

private:

    void first_test();

    void second_test();

};

void ExampleTestSuite::first_test()

{

    TEST_ASSERT(1 + 1 == 2)

    TEST_ASSERT(1 != 0);

}

void ExampleTestSuite::second_test()

{

    TEST_ASSERT_DELTA(0.5, 0.7, 0.3);

    TEST_ASSERT_DELTA(0.5, 0.7, 0.1);

}

 

int main () {

 

ExampleTestSuite test;

std::ofstream ofile;

 ofile.open("testhtml.html");

 Test::HtmlOutput output;

 test.run(output);

 output.generate(ofile,true,"Lee");

 return 0;

}

 

 

//输出普通的日志文件形式

#include <cstdlib>

#include <cstring>

#include <iostream>

#include <fstream>

 

#include "cpptest.h"

 

using namespace std;

 

class myTest : public Test::Suite

{

public:

  void function1_to_test_some_code( ) {};

  void function2_to_test_some_code( ) {};

 

  myTest( ) {

      TEST_ADD (myTest::function1_to_test_some_code);

      TEST_ADD (myTest::function2_to_test_some_code);

  }

};

 

class failTest1 : public Test::Suite

{

         void always_fail()

         {

           TEST_FAIL ("This always fails!\n");

         }

public:

       failTest1() { TEST_ADD(failTest1::always_fail); }

};

 

int main () {

  failTest1 test1;

  std::ofstream ofile;

  ofile.open("test.log");

  Test::CompilerOutput output(

      Test::CompilerOutput::MSVC, ofile);

 return test1.run(output) ? 1 : 0;

 

}

 

 

编译源文件形如:g++ -o main -I /home/Lee/CppTest/include/ -L /home/Lee/CppTest/lib/ main.cpp -lcpptest

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ 的测试框架通常会使用桩函数(Stub)来模拟被测对象的行为,以便在测试中进行隔离和控制。对于一些常见的函数,cpptest 可以自动生成桩函数,从而简化测试的编写过程。下面是 cpptest 如何自动生成桩函数的示例: 假设我们有一个计算器类 Calculator,其中包含一个 `add()` 方法用于计算两个整数的和。我们想要测试这个方法,但是它依赖于另外一个类的方法,因此我们需要使用桩函数来模拟这个方法的行为。 ```c++ class Calculator { public: Calculator(Adder* adder) : adder_(adder) {} int add(int a, int b) { return adder_->add(a, b); } private: Adder* adder_; }; class Adder { public: virtual ~Adder() {} virtual int add(int a, int b) = 0; }; class RealAdder : public Adder { public: int add(int a, int b) override { return a + b; } }; ``` 在使用 cpptest 进行测试时,我们可以使用 `CPPTEST_AUTO_STUB` 宏来自动生成桩函数。这个宏会根据被测函数的参数类型和返回值类型自动生成一个桩函数,并且将其添加到测试代码中。 ```c++ TEST(CalculatorTest, AddTest) { // 自动创建桩函数 CPPTEST_AUTO_STUB(int, Adder::add, (int a, int b), (a, b), (0)); // 模拟 Adder::add() 方法 Adder* adder = new AdderStub(); EXPECT_CALL(*adder, add(2, 3)).WillOnce(Return(5)); // 测试 Calculator::add() 方法 Calculator calculator(adder); EXPECT_EQ(calculator.add(2, 3), 5); } ``` 在这个示例中,我们使用 `CPPTEST_AUTO_STUB` 宏来自动生成 Adder::add() 方法的桩函数,这个桩函数的返回值类型为 int,参数类型为两个 int 类型的参数。在测试中,我们模拟了 Adder::add() 方法的行为,并且测试了 Calculator::add() 方法的正确性。 总的来说,使用 cpptest 自动生成桩函数可以简化测试代码的编写过程,提高测试的效率和可靠性。但是需要注意的是,自动生成的桩函数可能无法满足所有的测试需求,需要在实际使用中加以验证和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值