在C和C++中测试代码

15

我不知道你们如何在编写每行代码时进行不同级别的测试:单元测试,集成测试等。

举个例子,如果你想对刚刚编写的函数进行单元测试,你会写另一个完整的主函数和 Makefile 进行测试吗?或者你会修改项目的主函数来测试该函数吗?还是你只是在调试模式下运行项目,在函数被调用之前停止并修改其参数的值?

我相信一定有一些方便而常见的方法大多数人都在使用,只有我不知道而已。

12个回答

9
Test Driven Development(TDD)的方法是先编写测试,查看它默认失败(即新测试通过失败),然后编写函数以通过测试。
这样,测试就不再是一个事后的想法,而是你开发方法论的核心。
因为你可能会在实现操作对象之前开发一个函数(方法),大多数TDD框架也提供生成“模拟”对象的工具,这些对象将在其类实际实现之前返回预期的结果。
我个人推荐使用Google TestGoogle Mock

7
比较CppTest和CppUnit,我会选择CppTest。CppTest的框架更少,更易于理解和实现。我个人喜欢看到main入口点。我还包括了Boost单元测试框架。它不是基于xUnit。我不是粉丝,但如果您已经在使用Boost库,那么将其合并起来会很好。

创建单元测试和测试套件的方便程度。CppUnit和CppTest都可以创建类方法的单元测试,其中类本身派生自某个工具提供的Test类。CppTest的语法略微简单,测试注册发生在类构造函数内部。对于CppUnit,需要额外的宏CPPUNIT_TEST_SUITE和CPPUNIT_TEST_SUITE_ENDS。

运行测试。CppTest只需在测试套件上调用run方法,而CppUnit使用一个名为TestRunner的单独类,其run方法被调用以运行测试。

扩展测试层次结构。对于CppTest,始终可以通过创建从旧类继承的新类来扩展先前的测试套件。新类将定义一些添加到单元测试池中的附加函数。只需在新类类型的对象上调用run方法即可。相比之下,CppUnit要求您使用宏CPPUNIT_TEST_SUB_SUITE以及类继承来实现相同的效果。

生成格式化输出。CppTest和CppUnit都具有自定义输出的能力。但是,虽然CppTest具有有用的预定义HTML输出格式化程序,但CppUnit没有。但是,CppUnit专门支持XML格式。两者都支持文本和编译器样式格式。

创建测试固件。要使用测试固件,CppUnit要求测试类派生自CppUnit :: TestFixture。您必须为设置和拆卸例程提供定义。对于CppTest,您只需要为设置和拆卸例程提供定义。这绝对是更好的解决方案,因为它使客户端代码简单化。•预定义实用程序宏支持。CppTest和CppUnit都具有可比较的宏集,用于断言、处理浮点数等。

头文件。CppTest要求您包含单个头文件,而CppUnit客户端代码必须包含多个头文件,例如HelperMacros.h和TextTestRunner.h,具体取决于所使用的功能。

http://www.ibm.com/developerworks/aix/library/au-ctools3_ccptest/index.html?ca=drs-


CPPTEST

#include “cppTest.h”

class myTestWithFixtures : public Test::Suite { 
  void function1_to_test_some_code( );
  void function2_to_test_some_code( );

  public: 
  myTestWithFixtures ( ) { 
      TEST_ADD (function1_to_test_some_code) {...}; 
      TEST_ADD (function2_to_test_some_code) {...}; 
  } 

  protected: 
    virtual void setup( ) { ... };
    virtual void tear_down( ) { ... };
}; 

int main ( ) 
{ 
  myTestWithFixtures tests; 
  Test::TextOutput output(Test::TextOutput::Verbose);
  return tests.run(output);
} 

http://www.ibm.com/developerworks/aix/library/au-ctools3_ccptest/index.html?ca=drs-


CPPUNIT

#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TextTestRunner.h>
#include <cppunit/extensions/HelperMacros.h>

class mystringTest : public CppUnit::TestFixture {
public:
  void setUp() { ... };
  void tearDown() { ... };

  void function1_to_test_some_code() { ... };
  void function2_to_test_some_code() { ... };

  CPPUNIT_TEST_SUITE( mystringTest );
  CPPUNIT_TEST( function1_to_test_some_code );
  CPPUNIT_TEST( function2_to_test_some_code );
  CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION( mystringTest );

没有宏
int main ()
{
  CppUnit::TestSuite* suite = new CppUnit::TestSuite("mystringTest");
  suite->addTest(new CppUnit::TestCaller<mystringTest>("checkLength",
                &mystringTest::checkLength));
  suite->addTest(new CppUnit::TestCaller<mystringTest>("checkValue",
                &mystringTest::checkLength));

  // client code follows next 
  CppUnit::TextTestRunner runner;
  runner.addTest(suite);

  runner.run();
  return 0;
}

http://www.ibm.com/developerworks/aix/library/au-ctools2_cppunit/


Boost单元测试框架

该文章介绍了Boost单元测试框架,它是一个C++单元测试框架,可以帮助开发人员编写和运行测试用例。使用这个框架可以快速、简便地测试代码,并且输出结果易于理解。
#include <boost/test/unit_test.hpp>

using namespace std;

struct CMyFooTestFixture
{
    CMyFooTestFixture() { ... } //SetUp
    ~CMyFooTestFixture() { ... } //TearDown

    void function1_to_test_some_code(CMyFoo& foo) { ... };
    void function2_to_test_some_code(CMyFoo& foo) { ... };
}

BOOST_FIXTURE_TEST_SUITE(MyFooTest, CMyFooTestFixture);

BOOST_AUTO_TEST_CASE(function1_to_test_some_code)
{
    CMyFoo foo;
    function1_to_test_some_code(foo);
}

BOOST_AUTO_TEST_CASE(function1_to_test_some_code2)
{
    CMyFoo foo;
    function1_to_test_some_code(foo);
}

BOOST_AUTO_TEST_SUITE_END();

http://www.beroux.com/english/articles/boost_unit_testing/


4

1
GoogleTest是一个不错且易于使用的软件包。它也有很好的文档支持。我转而使用cpptest的唯一原因是谷歌的宏非常复杂,导致我的IDE代码解析和自动完成失败。这不是GoogleTest的问题,但仍然是一个问题。 - Jay

3

xUnit是一个单元测试模块家族。x被替换为所使用框架语言的字母。该家族目前包括:

我曾经在使用CppUnit的项目中取得了良好的结果。最近,我尝试将其集成到自动构建环境中(例如Hudson),但遇到了许多障碍。

理想情况下,构建应自动构建并运行单元测试。在这种情况下,代码从测试环境运行(因此具有自己的主循环)。在我的案例中,另一个复杂性是我使用嵌入式系统;printf并不总是可行的。如果您在PC上运行,CUnit和CppUnit可以帮助您实现良好的单元测试。请查看如何使用结果;持续集成系统将大大提高您的效率。

另一个值得一看的框架是Maestra。它依赖于C99(Microsoft从未实现过,但对于gcc来说非常好!)


3
我学会了喜欢googletest/googlemock。它们是一组非常强大且易于使用的工具。它们的wiki页面上还有很多文档可供参考。
googletest: code.google.com/p/googletest/wiki/GoogleTestPrimer googlemock: code.google.com/p/googlemock/wiki/ForDummies

2
我通常这样做:

我通常采用以下方式:

int foo(int bar) {
   ...
}

#ifdef FOO_UNITTEST
int main(int argc, char *argv[]) {
    // tests
}
#endif

我有一个makefile,其中CFLAGS中包含-DFOO_UNITTEST

这虽然有些笨拙,但你总是可以将测试代码放在代码旁边。


2

我喜欢 googletestgooglemock。这个框架非常容易配置,并且文档很详细。在这里,有一个很好的介绍。


1

1
你还应该考虑运行一个测试覆盖率工具,以查看你的测试是否实际上足够涵盖代码,从而成为一组好的测试。 请参见SD Test Coverage for C

1

我喜欢UnitTest++。它非常容易设置,编写测试比典型的CppUnit更容易。每个测试的编码开销非常小,因此您更有可能编写它们!

至于我测试什么,我倾向于测试类的公共接口,但如果我只是进行一些重构并将其拆分为较小的函数,则不会为每个函数编写一组测试 - 它们应该由测试公共接口的测试覆盖。只要公共接口根据测试工作正常,世界上就一切安好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接