【发布时间】:2009-09-17 14:16:54
【问题描述】:
我不知道你们每次编写代码时如何测试代码并进行不同级别的测试:单元测试、集成测试……
比如对你刚刚写的一个函数进行单元测试,你是不是又写了一整套main函数和Makefile来测试呢?或者你是否修改你的项目的主要功能来测试功能。或者你只是在调试下运行你的项目,然后在函数即将被调用的地方停下来修改它的参数值?
我相信一定有一些大多数人都在使用但我不知道的方便和常用的方法。
【问题讨论】:
我不知道你们每次编写代码时如何测试代码并进行不同级别的测试:单元测试、集成测试……
比如对你刚刚写的一个函数进行单元测试,你是不是又写了一整套main函数和Makefile来测试呢?或者你是否修改你的项目的主要功能来测试功能。或者你只是在调试下运行你的项目,然后在函数即将被调用的地方停下来修改它的参数值?
我相信一定有一些大多数人都在使用但我不知道的方便和常用的方法。
【问题讨论】:
测试驱动开发(TDD)的做法是先编写测试,看默认失败(即新的测试失败就成功),然后编写函数通过测试。
通过这种方式,测试不再是事后的想法,而是开发方法的核心。
因为您可能在实现一个函数(方法)之前就已经开发了一个函数(方法),所以大多数 TDD 框架还提供了一种用于生成“模拟”对象的工具,这些对象将在它们的类实际实现之前返回预期的结果。
我个人推荐Google Test和Google Mock。
【讨论】:
比较 CppTest 和 CppUnit 我会选择 CppTest。 CppTest 隐藏的框架更少,IMO 更易于理解和实施。我个人喜欢看到 main 入口点。我还包括了 Boost 单元测试框架。它不是基于xUnit。我不是粉丝,但如果你已经在使用 Boost 库,加入它会很好。
CppTest 与 CppUnit
•易于创建单元测试和测试 套房。 CppUnit 和 CppTest 都创建 类方法的单元测试,使用 类本身派生自一些 工具提供的测试类。语法 因为 CppTest 稍微简单一些, 但是,通过测试注册 发生在课堂上 构造函数。在 CppUnit 的情况下, 额外的宏 CPPUNIT_TEST_SUITE 和 需要 CPPUNIT_TEST_SUITE_ENDS。
•运行测试。简单的 CppTest 在测试中调用 run 方法 套件,而 CppUnit 使用单独的 运行方法为的 TestRunner 类 调用以运行测试。
•扩展测试层次结构。在 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 单元测试框架
#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();
【讨论】:
【讨论】:
xUnit 是一系列单元测试模块。 x 替换为所用框架语言的字母。该家庭目前包括:
我曾在使用 CppUnit 的项目中工作过,效果很好。最近我尝试将其集成到自动构建环境(即 Hudson)中,但遇到了许多障碍。
理想情况下,构建会自动构建并运行单元测试。在这种情况下,代码从测试环境运行(因此有自己的主循环)。就我而言,一个额外的复杂情况是我使用嵌入式系统。 printf 并不总是可行的。我希望如果您在 PC 上运行,CUnit 和 CppUnit 可以帮助您实现良好的单元测试。请看如何使用结果;持续集成系统将大大提高您的效率。
另一个值得一看的框架是Maestra。它依赖于 C99(微软从未实现过,但对于 gcc 来说它很棒!)
【讨论】:
我学会了爱上 googletest/googlemock。它们是一个非常强大的组合并且易于使用。他们的 wiki 页面上也有很多可用的文档。
谷歌测试: code.google.com/p/googletest/wiki/GoogleTestPrimer
谷歌模拟: code.google.com/p/googlemock/wiki/ForDummies
【讨论】:
我通常这样做:
int foo(int bar) {
...
}
#ifdef FOO_UNITTEST
int main(int argc, char *argv[]) {
// tests
}
#endif
我有一个makefile,它在CFLAGS 中有-DFOO_UNITTEST。
这很笨拙,但您总是将测试放在代码旁边。
【讨论】:
我喜欢googletest 和googlemock。真的很容易配置和有据可查,here 有一个很好的框架介绍。
【讨论】:
【讨论】:
您还应该考虑运行一个测试覆盖率工具,看看您的测试是否真的使用了足够多的代码来成为一组好的测试。 见SD Test Coverage for C。
【讨论】:
我爱UnitTest++。它真的很容易设置,并且编写测试比使用典型的最爱 CppUnit 更容易。每个测试的编码开销都非常小,因此您更有可能编写它们!
至于我测试的内容,我倾向于测试类的公共接口,但如果我只是进行一些重构并将其拆分为更小的函数,我不会为每个函数编写一组测试 -它们应该包含在测试公共接口的测试中。只要公共接口按照测试工作,一切都很好。
【讨论】:
我使用boost.test。我从 cppunit 移到它,这很好,但太像 java,这是一个问题,因为 junit 使用反射来查找和运行你的测试,因为这在 c++ 中不可用,你必须使用宏来注册你的测试,在 cppunit 中你必须在三个不同的地方声明、注册和定义您的测试。 boost.test 让您可以在一个语句中声明、注册和定义您的测试,这非常好。
我的一般方法是对新代码进行 TDD,并尝试对遗留代码明智地使用单元测试。我特别是在不同平台上测试任何不同的代码,以确保它们的行为相同并继续表现相同。
我构建我的项目,以便每个库或可执行项目也有一个单元测试项目。对于可执行测试,我在测试项目中包含可执行源文件(main 除外)并将我的测试添加到新文件中。对于库测试,我通常只链接到库,除非我测试 dll 的私有部分,然后我使用可执行方法。
使用CMake 使您能够抽象源项目和测试项目之间的任何重复。 CTest 还与任何将测试打包成可执行文件的单元测试框架很好地集成在一起。这使您可以一次性运行解决方案中的所有测试可执行文件并报告结果摘要。它还与名为 Cdash 的持续集成框架集成。
关于 TDD 的说明,很多人将其视为测试驱动开发,但也可以是测试驱动设计。这是专注于敏捷设计的一种非常好的方式,使用 TDD 来设计我的软件以及编写它真的让我大开眼界。
【讨论】:
我通常从像MinUnit 这样复杂的东西开始,然后构建一组适合我正在处理的项目的宏,以适应该项目的约定。
每个组件都有一个可执行文件,用于构建和运行该组件的单元测试。
我不使用调试器,因为这需要人工干预,因此您不能将其用于自动回归。我不修改主要的可执行文件,因为它通常涉及许多组件并且非常复杂,没有大量的宏来打开和关闭位。使用 make 创建新目标非常容易。
【讨论】: