在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉。这种感觉来源于学习者不知道一个MFC程序是如何运行起来的(即一个MFC程序的执行流程)和MFC程序的设计思想和机制,即使是写过Windows程序的学习者,也会感到非常迷惘并且无从下手。而这种感觉的出现会使大家认为自己离开了书本上的例子就无法设计编制程序。下面我就来说一说一个MFC具体是如何被执行的。在阅读本文之前,你要有一定的Windows程序设计基础,知道Windows程序的运行流程,如不清楚,可先看看我写的这篇文章——解说一个简单的Win32程序。
1.简单测试TEST
#include <gtest/gtest.h> int Factorial( int n ) { if(n==2) return 100; //故意出个错,嘻嘻 return n<=0? 1 : n*Factorial(n - 1); } //用TEST做简单测试 TEST(TestFactorial, ZeroInput) //第一个参数是测试用例名,第二个参数是测试名:随后的测试结果将以"测试用例名.测试名"的形式给出 { EXPECT_EQ(1, Factorial(0)); //EXPECT_EQ稍候再说,现在只要知道它是测试两个数据是否相等的就行了。 } TEST(TestFactorial, OtherInput) { EXPECT_EQ(1, Factorial(1)); EXPECT_EQ(2, Factorial(2)); EXPECT_EQ(6, Factorial(3)); EXPECT_EQ(40320, Factorial(8)); } int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc,argv); //用来处理Test相关的命令行开关,如果不关注也可不加 RUN_ALL_TESTS(); //看函数名就知道干啥了 std::cin.get(); //只是让它暂停而已,不然一闪就没了 return 0; }
2.多个测试场景需要相同数据配置的情况,用TEST_F
【摘要】在本文中,作者根据之前使用gtest框架进行测试的经验,总结了一些使用方式和案例。 在这些案例中,我们可以了解到gtest框架的基本使用方法以及在我们日常测试中的应用,同时也能促进我们对于百度的btest的了解。在我们之后的测试工作中,可以根据各个项目的特点以及gtest、btest等测试框架的功用,进行协调和因势利导,将更多框架产品用于我们的测试工作中,使我们的测试工作更加正规、更加高效、更加可依赖。由于作者能力有限,文中如果有一些不够清晰不够全面的地方,欢迎指正。
【关键词】gtest, cxxtest, TestSuite, TestCase
1、引言
之所以要向大家介绍测试框架,是因为我们在测试的过程中,会越来越体会到测试规范化与可重用性的重要。一方面,我们要让测试的过程可管理化,要让我们自己、RD、其他同学都能够看懂我们测的是什么,是怎么来测的;另一方面,我们不可能一个项目总是一两个同学一直测下去,如果换到其他同学,需要有一种方式让这种传承更加通用、更加高效。如果能够让一个项目组写测试case使用相同的框架、相同的风格,那么可以避免我们的测试过于山寨,可以让测试的设计和执行更加正规化。
我们选择一些第三方的开源的测试框架也是有原因的:
首先,如果我们的公司内部自己实现了一套测试框架,固然可定制性更高,但是之后就会发觉相当于把测试框架重新做了一遍,业界有很多测试框架,已经并且正在接受全世界程序员的使用、检测和改进,比我们公司内部一个项目的力量要大很多。
其次,正因为这些测试框架是开源的,所以虽然这些框架已经提供了比较完善和简单易用的功能,如果我们需要加上额外的功能,仍然可以在上面进行二次开发,相当于站在了巨人的肩膀上。
现在百度ATD团队也在gtest的基础上发布了btest,可以让gtest的使用更加便捷;btest的运行方式和btest是一致的,如果我们对gtest有更多的了解,那么使用btest也会更加得心应手。下文中的所有的内容都适用于btest。
2、cppunit, cxxtest与gtest框架
在进入百度后,我主要测试的项目是一个后台服务程序,在测试自动化方面做的主要工作是使用shell, python以及c++编写一些客户端、桩模块以及测试驱动程序,来测试这个后台服务程序的各种行为是否符合我们的预期,并且在屏幕中打印出结果:Pass或者Fail. 很幸运的,我后来承担了一个lib库中的一个接口函数的测试,这个接口函数主要是进行URL的解析,虽然这个接口函数的代码量不多,测试工作量也不大,但是却是我之前没有接触过的测试范畴。我接到这个项目时,首先的想法就是这种函数级别的测试非常类似于RD经常要做的单元测试,我可以使用一些单元测试框架来规范我的测试,提高测试的效率,并且使得我的测试case、测试代码等等的复用性更高。
于是我就开始调研使用哪一个测试框架来进行我的测试。与Java语言中的JUnit或者testNG不同,C和C++中的单元测试框架并没有这么普遍的工具,有一点百花齐放的局面。
我首先调研的是cppunit,这是一个与JUnit类似的框架,我在大学时也接触过,但是这个框架很陈旧了,并且有着一些缺点,例如一些类可以消失,一些类名应该修改,一些宏定义应该修改,帮助很少很乱等,这里就不再一一赘述,感兴趣的同学可以百度一下。因为cppunit有着一系列的缺点,cppunit的鼻祖之一重写了一套C/C++单元测试框架,这就是cxxtest。与cppunit相比,可以说cxxtest具有如下一些优点:不需要RTTI(运行时间类型信息);不需要成员模板功能;不需要异常处理;不需要任何外部函数库(包括内存管理、文件/控制台的输入/输出和图形库等);它完全是作为一套头文件的集合而进行发布的。另外由于由于cppunit带有Make文件, 所以只能用在主要的操作系统中,而应用到不常见操作系统中源代码及Make文件修改的工作量就会很大。cxxtest不带Make文件, 所以也可用于其他操作系统中,具有更好的可移植性和可用性。Gtest虽然也使用Makefile文件,但是通过libtool动态加载来实现,可以支持Supports Linux, Windows, Mac OS, 以及其他操作系统。
当然cxxtest也有一些缺点,例如需要用到perl或者python对测试代码的头文件进行文法扫描,生成可执行代码,准备工作比较麻烦。并且cxxtest的绝大多数功能都可以使用gtest来完成,所以一般情况下就更加推荐gtest,一步到位。在百度ATP的自动化测试工具推荐名单中,gtest也是作为一个推荐的工具来给出的,并且指出了cxxtest的使用可以转为用gtest代替。下面先给大家列出一些cxxtest和gtest之间的比较,这样大家就有个直观的印象。
图1 gtest和cxxtest的比较
在这次lib库的测试中,因为com组的qa同事使用的是cxxtest框架,所以我也使用了cxxtest框架,但是根据从网上了解的gtest的种种优点,我也学习了gtest的使用,在这里就主要向大家推荐gtest的使用,这样更能一步到位,免得总是不停地更换测试框架。
首先介绍一下gtest测试框架。这是Google的开源C++单元测试框架,是遵循 New BSD License (可用作商业用途)的开源项目。据说google内部的大多数C++代码都已经使用这个测试框架进行单测,gtest 可以支持绝大多数大家所熟知的平台。Gtest的使用较为方便,与 CppUnit 不同的是,gtest 可以自动记录下所有定义好的测试,不需要用户通过列举来指明哪些测试需要运行。
要了解gtest测试框架,一个很方便的学习资源就是gtest的官方网站 http://code.google.com/p/googletest/ . 在这个网址,读者也可以下载到gtest的源代码,用于阅读学习乃至在上面的改进。在本文中,我先根据自己的使用经验,给大家做一些介绍。
3、gtest 简单示例
我们就开始着手试一下gtest的基本功能吧,首先从http://code.google.com/p/googletest/ 这个网站下载下gtest的压缩包,解压到Linux环境的测试机器。然后我们进入解压后的gtest目录,首先 ./configure 然后 make ,然后我们就可以使用这个gtest测试框架了。
我们写一段简单的例子,try.cpp:
//try.cpp
#include "gtest/gtest.h"
namespace
{
// The fixture for testing class Foo.
class FooTest : public ::testing::Test
{
protected:
FooTest()
{
// You can do set-up work for each test here.
}
virtual ~FooTest()
{
// You can do clean-up work that doesn't throw exceptions here.
}
virtual void SetUp()
{
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown()
{
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for Foo.
};
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, ZeroEqual)
{
EXPECT_EQ(0,0);
}
// Tests t