C++单元测试一:并非看上去那么简单——几个很实际的问题

理想与现实
为Java和C#做单元测试,基本上都有比较统一的工具、模式可用,IDE的支持也非常到位;可是到
了C++这里,一切就变的那样的“不走寻常路”,各种单元测试框架盛行,例如CppUnit, 
CppUnitLite, CxxUnit,Google Test等等,以及微软在VS2012中也加入了对原生C++代码单元测
试的支持MSTest。面对如此诸多的测试框架,选择哪一个其实无所谓,只要顺手就好,用习惯了
,什么都好;因为,单元测试的代码总归还是要自己来写——无论你用哪一个框架。
以前写过不少代码,可是都没怎么注意单元测试,现在终于认真对待起来,就开始在网络上搜寻资
料,看看各种框架下的单元测试如何写。幸运的是,这方面的资料真不少,很多框架也都会带有示
例或者文档告诉你怎么写;不幸的是,这些文档或者示例都太远离工程实践,他们一般遵循一个这
样的模式:写出一个待测类CMyClass,定义一定的成员变量和方法,然后给出针对CMyClass的
测试类如CMyClassTest;呵呵,看起来示例是够好了,可是很少涉及到这样的实际问题: 
工程实践中,一个项目往往有很多类,而且相互之间,总有这或多或少的依赖、包含等关系;
 实际的测试项目,该如何组织被测代码和测试代码(注意,这里所说的组织不是指单元测试数据
如何组织,而是指工程中的测试代码和产品代码的组织)
 被测代码如何引入测试工程中
 一个测试工程如何测试多个被测类
实际的代码
好吧,我们来看一下一个“较为”实际的工程以及我在为该工程编写单元测试过程中所遇到的问题
;该工程的代码来自于boost.asio中的一个示例代码:
http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/chat/chat_server.cpp,

我把该cpp中的几个类分拆开了,放在一个VisualStudio 2012工程中,代码结构看起来是:
其中几个主要类Message,ChatSession,ChatRoom之间的关系如下图:
在我做单元测试过程中,首先从软柿子Message入手,然后为ChatRoom写UT。所以,先把这两
个类的相关代码贴上来;其实贴不贴代码无关紧要,上面的类图已经可以说明问题,不过为了方便

较真,还是贴出来吧。

Message类代码

/// Message.h
#pragma once  /// class represents the message transferred between the client and the server
/// the message consists of two part: header + message body
/// the header part, 4 bytes, stores the length of the message body
/// the max length of the message box is : 512  class Message
{
public:  enum { HeaderLength = 4, MaxBodyLength = 511 };  public:  Message(void);  ~Message(void);  public:  void EncodeHeader();  bool DecodeHeader();  const char* Data() const;  char* Data() ;  const char* Body() const;  char* Body() ;  int SetData(const char* src, const int srclength);  int Length() const;  int BodyLength()const;  void Reset();  private:  void CheckBodyLength();  private:  /// stores the whole message  char Data_[HeaderLength + MaxBodyLength + 1];  /// the body length  int BodyLength_;
};  /// Message.cpp
#include "Message.h"
#include <cstdio>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include "TraceLog.h"  Message::Message(void)
{  Reset();
}     Message::~Message(void)
{
}  void Message::CheckBodyLength()
{  BodyLength_ = BodyLength_ > MaxBodyLength ? MaxBodyLength : BodyLength_;
}  void Message::EncodeHeader()
{  /// Check the body length  CheckBodyLength();  /// wirte the body length to the message header  /// we make sure that the buffer is enough after we call CheckBodyLength()  ::_snprintf_s( Data_, HeaderLength, HeaderLength, "%d", BodyLength_ );
}  bool Message::DecodeHeader()
{  int bodyLength = 0;  bool ret = false;  /// get the message body length from the message  try  {  char buf[HeaderLength + 1] = "";  std::strncat( buf, Data_, HeaderLength );  bodyLength = boost::lexical_cast<int> (buf);  if( bodyLength > MaxBodyLength )  {  bodyLength = MaxBodyLength;                }  else  {  ret = true;  }  }  catch(boost::bad_lexical_cast& e)  {  /// cast error happens  bodyLength = 0;  TraceLog::WriteLine("Message::DecodeHeader(),error:%s, orinal message:%s",
e.what(), Data_ );  }  /// set the value and return  BodyLength_ = bodyLength;  return ret;
}  char* Message::Data()
{  return Data_ ;
}  const char* Message::Data() const
{  return Data_ ;
}  char* Message::Body()
{  return Data_ + HeaderLength;
}  const char* Message::Body() const
{  return Data_ + HeaderLength;
}  int Message::SetData(const char* src, const int srclength)
{  /// check the length of source  int length = srclength;  if( length > MaxBodyLength )  {  length = MaxBodyLength;  }  /// copy the data into the local buffer  /// std::snprintf is unavailable in this c++ compiler  int ret = ::_snprintf_s(Data_+HeaderLength, MaxBodyLength + 1, length, "%s", src );     /// set the length of the message body  BodyLength_ = length;  /// return the length of copied  return ret;
}  int Message::Length() const
{  return BodyLength_ + HeaderLength;
}  int Message::BodyLength() const
{  return BodyLength_;
}  void Message::Reset()
{  BodyLength_ = 0;  /// just for using the lamda  std::for_each(Data_, Data_ + HeaderLength + MaxBodyLength + 1, [](char& p) { p =
'\0'; } );
}

ChatRoom类代码
/// ChatRoom.h  
#pragma once  
#include "ChatSession.h"  
#include "Message.h"  
#include <set>  
#include <queue>  
   
/// class that manages the clients  
/// deliver the messages from one client to the others  
   
class ChatRoom  
{  
public:  
      ChatRoom(void);  
      ~ChatRoom(void);  
   
public:  
      /// a client joins in the room  
      void Join(ChatParticipantPtr participant);  
   
      /// a client leaves the room  
      void leave(ChatParticipantPtr participant);  
   
      /// deliver the message from one client to all of the users in the room  
      void Deliver(const Message& msg);  
   
private:  
      /// all of the participants are stored here  
      std::set<ChatParticipantPtr> Participants_;  
   
      /// recent messages  
      /// questions, how to synchronize this object in threads  
      typedef std::deque<Message> MessageQueue;  
      MessageQueue RecentMessages_;  
      enum { MaxRecentMsgs = 100 };  
};  
      
/// ChatRoom.cpp  
#include "ChatRoom.h"  
#include <boost/bind.hpp>  
#include <algorithm>  
#include "TraceLog.h"  
   
ChatRoom::ChatRoom(void)  
{  
}     
   
ChatRoom::~ChatRoom(void)  
{  
}  
   
/// a client joins in the room  
void ChatRoom::Join(ChatParticipantPtr participant)  
{  
      TraceLog::WriteLine("ChatRoom::Join(), a new user joins in");  
   
      /// add into the queue  
      Participants_.insert( participant );  
   
      /// sending the recent message to the client  
      std::for_each(RecentMessages_.begin(), RecentMessages_.end(),  
            boost::bind( &ChatParticipant::Deliver, participant, _1 ) );  
}     
   
/// a client leaves the room  
void ChatRoom::leave(ChatParticipantPtr participant)  
{  
      TraceLog::WriteLine("ChatRoom::leave(), a user leaves");  
   
      /// remove it from the queue  
      Participants_.erase( participant );  
}     
   
/// deliver the message from one client to all of the users in the room  
void ChatRoom::Deliver(const Message& msg)  
{  
      TraceLog::WriteLine("ChatRoom::Deliver(), %s", msg.Body() );  
   
      /// add the msg to queue  
      RecentMessages_.push_back( msg );  
   
      /// check the length  
      while( RecentMessages_.size() > MaxRecentMsgs )  
      {  
            RecentMessages_.pop_front();  
      }  
   
      /// deliver the msg to clients  
      std::for_each(Participants_.begin(), Participants_.end(),  
            boost::bind( &ChatParticipant::Deliver, _1, boost::ref(msg) ) );  
}

开始单元测试
由于到手了VisualStudio 2012,这货已经原始支持了C++Native代码的单元测试,就用这货开始做
UT吧。

如何引入被测代码
好了,我们开始单元测试。首先创建一个C++单元测试的工程,这个很easy。接着我们就要让测
试工程能够“看到”被测的代码,这如何搞呢?有这样几种方法:

如果被测代码是静态库或者动态库,包含对应的.h文件,让测试工程链接DLL及LIB,这样测试工
程。
或者,让测试工程链接对应的obj文件,直接编译进测试工程
或者,直接把被测是的代码,如上述的Message.h和Message.cpp包含进测试工程(注意这里不
要拷贝一份Message.h和Message.cpp,用“Add->ExsitingItem”将他们包含进去,这样只保
留一份代码)
 或者在单元测试代码文件,如TestMessage.cpp中直接用#include把Message.h和Message.cpp
包含进来,如: 
               #include "../ChatroomServer/ChatRoom.h"

#include "../ChatroomServer/ChatRoom.cpp"

上面这几种方法,其实原理都是一样的,反正就是让测试工程能够看到到被测的代码,我们使用把

被测代码引入测试工程的方法,这样测试工程的代码结构看起来是这样:

Ok,现在在测试工程里面,可以看到Message类的声明和定义了,然后你的单元测试代码,该怎

么写,就怎么写了。

一个测试工程只能测一个类吗?
使用VS2012中的单元测试框架,写完了对Message的的单元测试,在TestExplorer中RunAll,一

切正常;好了,至此,一切还算顺利,那我们继续吧,来对ChatRoom类编写单元测试;

继续按照前面的方法,我们很容易让测试工程看到ChatRoom的被测代码;然而从ChatRoom的

实现来看,这个类和Message类有着关联关系,而且在ChatRoom的方法中,也的确使用了

Message类的方法,从单元测试的角度来看,我们应该将他们俩之间的关系隔断,从而保证我们

只对ChatRoom进行测试,那这样,我们就需要Mock一份Message的实现。

可是问题来了,由于之前我们在测试Message类的时候,已经引入了Message.cpp文件,使得测

试工程中,已经有了一份Message的实现,那么我们如何再能为Message提供一份“伪”实现呢

??(使用其他几种引入方式,结果都是一样的)

是的,惯用的DependencyInjection在这里不起作用。查了不少资料,也没找到一个像样的说明

如何解决这个问题;现在唯一可以采用的,就是在一个测试工程里面,只测试一个被测类,虽然可

以工作,但是,未免又过于繁琐和“愚蠢”,那聪明的方法,又是什么呢?不瞒你说,这正是目前

困扰我的问题之一。

追加一个后记:

其实,在关于如何为Message提供一份“伪”实现的问题上,原来想法是在测试工程中包含

Message的头文件,然后在测试工程里面,直接写Message::Message()等方法,事实上是在测试

工程里面定义一个Message的实现;这样由于我们已经引入了Message的真正实现,从而必然导

致链接器报符号重复定义的错误,因此,这种思路并不可行,故而强迫我去创建另外一个工程;后

来想一想,其实也不必真的去创建一个新工程,他们是可以在一个工程里面完成的,方法是新建一

个MockMessage类,让他从Message继承下来,然后重新定义自己想Mock的方法就可以了。但

是,这种方法创建出来的Mock,还是真的Mock吗,他首先已经是一个“真”的Message了啊?
========

C/C++单元测试理论精要(一)

http://blog.csdn.net/easytdd/article/details/5484405

内容介绍

本系列文章根据《单元测试与VU2.6应用》视频讲座的理论部分整理而成,主要讨论四个问题

:为什么需要单元测试?怎样征服可测性难题?怎样才能高效率测试?怎样保证测试效果?重点阐

述单元测试的关键问题,不是一般概念,适合于对单元测试有一定了解的读者。

在选择工具和实施单元测试前,我们应该对相关理论有一个系统的了解,特别是将会遇到哪些

难题,如何解决,要心里有数,否则的话,很可能劳民伤财,半途而废。如果只会测试加法函数或

者三角形函数之类的独立小程序,就以为可以做单元测试了,那就像一个人刚学会走路,就去长途

跋涉。

本文介绍的是针对企业项目的单元测试。企业项目具有两个特点:项目复杂,时间紧张。项目

复杂,意味着测试时会遇到很多难题;时间紧张,要求我们不但要保证测试效果,还要尽可能高效

率。本文不是泛泛而谈,而是针对企业项目的两个特点,努力揭示本质性的问题,并提出解决办法

,对于常识性的问题,将比较简略的带过。使用的工具是Visual Unit 2.6,本文主要不是介绍工具

,而是介绍问题所在和解决办法,涉及到工具,只是为了具体的展示解决办法,也为了说明,这些

办法都是可行的,并非空谈。

第1章 为什么需要单元测试?
1.1 从代码特性看单元测试的必要性

代码有一个很基本的特性,是什么呢?对数据分类处理。一个判定,就是一次分类。如果判定

中又嵌套了判定的话,分类次数就会翻倍。循环判定也是一种分类。

如果一个分类遗漏的话,也就是说,某种输入没有处理,会怎么样呢?一个Bug诞生了!如果

一个分类的处理不正确,又会怎么样呢?又一个Bug诞生了!

一个函数要做到没有错误,要保证两点:分类完整,也就是各类可能输入都要考虑到;

处理正确,也就是每一类输入都要进行正确的处理。做到了这两点,就可以说,函数的功能逻辑是

正确的。函数的功能逻辑就是对数据的分类以及处理。

那么,怎样才能全面地检测程序的功能逻辑呢?调试,是临时的,不做记录,另一方面,调试

的工作方式是拦截数据,并不是所有输入分类都能拦截得到的,如果一个函数有十类输入,调试能

覆盖五六个就不错了。系统测试,不针对具体的函数,无法做到对具体函数的输入分类覆盖。要全

面地检测程序的功能逻辑,必须把输入分类全部列出来。并检测程序是否处理了这些输入,处理是

否正确。这就是单元测试。

说明
本系列文章根据《单元测试与VU2.6应用》视频讲座的理论部分整理而成,PPT及视频下载:
PPT下载:http://download.csdn.net/source/2246006
视频part1: http://download.csdn.net/source/2246273
视频part2: http://download.csdn.net/source/2246345
视频part3: http://download.csdn.net/source/2246364
本系列文章及视频讲座介绍的是单元测试理论的精要部分,详细内容请阅读《C/C++单元测试实

用教程》 
========

玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架

http://www.cnblogs.com/coderzh/archive/2009/04/12/1434155.html
一、前言

上一篇我们分析了gtest的一些内部实现,总的来说整体的流程并不复杂。本篇我们就尝试编写一

个精简版本的C++单元测试框架:nancytest ,通过编写这个简单的测试框架,将有助于我们理

解gtest。

二、整体设计

使用最精简的设计,我们就用两个类,够简单吧:

1. TestCase类
包含单个测试案例的信息。

2. UnitTest类

负责所有测试案例的执行,管理。

三、TestCase类

TestCase类包含一个测试案例的基本信息,包括:测试案例名称,测试案例执行结果,同时还提

供了测试案例执行的方法。我们编写的测试案例都继承自TestCase类。

复制代码
class TestCase
{
public:
    TestCase(const char* case_name) : testcase_name(case_name){}

// 执行测试案例的方法
    virtual void Run() = 0;

int nTestResult; // 测试案例的执行结果 
    const char* testcase_name; // 测试案例名称
};
复制代码

四、UnitTest类

我们的UnitTest类和gtest的一样,是一个单件。我们的UnitTest类的逻辑非常简单:

1. 整个进程空间保存一个UnitTest 的单例。

2. 通过RegisterTestCase()将测试案例添加到测试案例集合testcases_中。

3. 执行测试案例时,调用UnitTest::Run(),遍历测试案例集合testcases_,调用案例的Run()方法

复制代码
class UnitTest
{
public:
    // 获取单例
    static UnitTest* GetInstance();

// 注册测试案例
    TestCase* RegisterTestCase(TestCase* testcase);
    
    // 执行单元测试
    int Run();

TestCase* CurrentTestCase; // 记录当前执行的测试案例
    int nTestResult; // 总的执行结果
    int nPassed; // 通过案例数
    int nFailed; // 失败案例数
protected:
    std::vector<TestCase*> testcases_; // 案例集合
};
复制代码
下面是UnitTest类的实现:

复制代码
UnitTest* UnitTest::GetInstance()
{
    static UnitTest instance;
    return &instance;
}

TestCase* UnitTest::RegisterTestCase(TestCase* testcase)
{
    testcases_.push_back(testcase);
    return testcase;
}

int UnitTest::Run()
{
    nTestResult = 1;
    for (std::vector<TestCase*>::iterator it = testcases_.begin();
        it != testcases_.end(); ++it)
    {
        TestCase* testcase = *it;
        CurrentTestCase = testcase;
        std::cout << green << "======================================"

<< std::endl;
        std::cout << green << "Run TestCase:" << testcase->testcase_name << std::endl;
        testcase->Run();
        std::cout << green << "End TestCase:" << testcase->testcase_name << std::endl;
        if (testcase->nTestResult)
        {
            nPassed++;
        }
        else
        {
            nFailed++;
            nTestResult = 0;
        }
    }

std::cout << green << "======================================"

<< std::endl;
    std::cout << green << "Total TestCase : " << nPassed + nFailed << std::endl;
    std::cout << green << "Passed : " << nPassed << std::endl;
    std::cout << red << "Failed : " << nFailed << std::endl;
    return nTestResult;
}
复制代码
五、NTEST宏

接下来定一个宏NTEST,方便我们写我们的测试案例的类。

复制代码
#define TESTCASE_NAME(testcase_name) \
    testcase_name##_TEST

#define NANCY_TEST_(testcase_name) \
class TESTCASE_NAME(testcase_name) : public TestCase \
{ \
public: \
    TESTCASE_NAME(testcase_name)(const char* case_name) : TestCase(case_name){}; \
    virtual void Run(); \
private: \
    static TestCase* const testcase_; \
}; \
\
TestCase* const TESTCASE_NAME(testcase_name) \
    ::testcase_ = UnitTest::GetInstance()->RegisterTestCase( \
        new TESTCASE_NAME(testcase_name)(#testcase_name)); \
void TESTCASE_NAME(testcase_name)::Run()

#define NTEST(testcase_name) \
    NANCY_TEST_(testcase_name)
复制代码

六、RUN_ALL_TEST宏

然后是执行所有测试案例的一个宏:

#define RUN_ALL_TESTS() \
    UnitTest::GetInstance()->Run();
七、断言的宏EXPECT_EQ

这里,我只写一个简单的EXPECT_EQ :

复制代码
#define EXPECT_EQ(m, n) \
    if (m != n) \
    { \
        UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0; \
        std::cout << red << "Failed" << std::endl; \
        std::cout << red << "Expect:" << m << std::endl; \
        std::cout << red << "Actual:" << n << std::endl; \
    }
复制代码

八、案例Demo

够简单吧,再来看看案例怎么写:

复制代码
#include "nancytest.h"

int Foo(int a, int b)
{
    return a + b;
}

NTEST(FooTest_PassDemo)
{
    EXPECT_EQ(3, Foo(1, 2));
    EXPECT_EQ(2, Foo(1, 1));
}

NTEST(FooTest_FailDemo)
{
    EXPECT_EQ(4, Foo(1, 2));
    EXPECT_EQ(2, Foo(1, 2));
}

int _tmain(int argc, _TCHAR* argv[])
{
    return RUN_ALL_TESTS();
}
复制代码

整个一山寨版gtest,呵。执行一下,看看结果怎么样:

九、总结

本篇介绍性的文字比较少,主要是我们在上一篇深入解析gtest时已经将整个流程弄清楚了,而现

在编写的nancytest又是其非常的精简版本,所有直接看代码就可以完全理解。希望通过这个

Demo,能够让大家对gtest有更加直观的了解。回到开篇时所说的,我们没有必要每个人都造一

个轮子,因为gtest已经非常出色的为我们做好了这一切。如果我们每个人都写一个自己的框架的

话,一方面我们要付出大量的维护成本,一方面,这个框架也许只能对你有用,无法让大家从中受

益。
gtest正是这么一个优秀C++单元测试框架,它完全开源,允许我们一起为其贡献力量,并能让更

多人从中受益。如果你在使用gtest过程中发现gtest不能满足你的需求时(或发现BUG),gtest

的开发人员非常急切的想知道他们哪来没做好,或者是gtest其实有这个功能,但是很多用户都不

知道。所以你可以直接联系gtest的开发人员,或者你直接在这里回帖,我会将您的意见转告给

gtest的主要开发人员。
如果你是gtest的超级粉丝,原意为gtest贡献代码的话,加入他们吧。   

本Demo代码下载:/Files/coderzh/Code/nancytest.rar

本篇是该系列最后一篇,其实gtest还有更多东西值得我们去探索,本系列也不可能将gtest介绍完

全,还是那句话,想了解更多gtest相关的内容的话:

访问官方主页:http://code.google.com/p/googletest/

下载gtest源码: http://code.google.com/p/googletest/downloads/list

系列链接:

1.玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest
2.玩转Google开源C++单元测试框架Google Test系列(gtest)之二 - 断言
3.玩转Google开源C++单元测试框架Google Test系列(gtest)之三 - 事件机制

4.玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

5.玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试 
6.玩转Google开源C++单元测试框架Google Test系列(gtest)之六 - 运行参数

7.玩转Google开源C++单元测试框架Google Test系列(gtest)之七 - 深入解析gtest
8.玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架
 
========

使用 Visual Studio 2015 对 C++ 代码运行单元测试

http://blog.csdn.net/lxf200000/article/details/51100094

代码写多了,往往规模会越来越大,这时候就有必要保证代码的稳定性了;不过我从网上看到的单

元测试貌似大多都是用的 JUnit, 难道 C++ 就没有了吗?我从网上找了一些方法试了下其实挺简

单的。下面我以一个示例作说明。(如果你有准备好的待测代码可直接看创建单元测试项目那里。

创建一个 Win32 空项目“stg”并添加下面的代码用作测试。这里我创建了一个结构体用来表示

一个物体,有X,Y,半径这些变量,还有一个用来表示另一个物体是否在它的半径内的函数,是

则返回1,否则为0。

【stg.h】

[cpp] view plain copy
#pragma once  
struct SpriteType  
{  
    SpriteType();  
    void SetValue(float, float, float, float);  
    int IsShotBy(SpriteType*);  
private:  
    float posX, posY, shotRadius, sensedRadius;  
    float _temp0;  
};

【stg.cpp】

[cpp] view plain copy
#include<cmath>  
#include"stg.h"  
  
SpriteType::SpriteType() :posX(0.0f), posY(0.0f), shotRadius(0.0f), sensedRadius(0.0f)  
{  
  
}  
  
void SpriteType::SetValue(float x, float y, float rshot, float rsensor)  
{  
    posX = x, posY = y, shotRadius = rshot, sensedRadius = rsensor;  
}  
  
int SpriteType::IsShotBy(SpriteType *pOtherSprite)  
{  
    _temp0 = sqrtf((pOtherSprite->posX - posX)*(pOtherSprite->posX - posX) +

(pOtherSprite->posY - posY)*(pOtherSprite->posY - posY));  
    return (int)(_temp0 < shotRadius);  
}

(为了简单我把 main 函数省略了。)
然后是创建单元测试项目。在这个解决方案中创建一个名为 stgTest 的单元测试工程(命名规则是

“项目名”+Test),

创建好后在引用中添加待测项目的引用(右键引用选择“添加引用项目”),

点确定,它应该会出现在单元测试工程的引用中。

然后在“stg”项目上右键选择属性,将其配置类型改为静态库(你可以在测试完后改回为应用程

序)

接下来找到单元测试的代码,其文件名默认为“unittest1.cpp”,最好也改为“stgTest.cpp”以

便区分;在这个代码中有一个默认的方法“TestMethod1”,它是由宏“TEST_METHOD”定义

的一个函数,这个函数就是用来作测试的,运行测试时它里面的代码会执行,测试结果由 Assert

中的一系列函数决定。另外两个

宏“TEST_METHOD_INITIALIZE”,“TEST_METHOD_CLEANUP”分别用于定义测试开始前与

结束后执行的函数,通常将函数名定义为“SetUp”和“TearDown”(或许是因为 JUnit 中就是

这样的?)。这里我将默认的“TestMethod1”改为“TestShot”(命名规则为 Test+“函数名

”),然后包含头文件 #include“..\stg\stg.h”,加入测试代码后形成下面这个样子:

【stgTest.cpp 中的部分代码】

[cpp] view plain copy
namespace AmIShotTest  
{         
    TEST_CLASS(UnitTest1)  
    {  
    private:  
        SpriteType *s1, *s2;  
    public:  
        TEST_METHOD_INITIALIZE(SetUp)  
        {  
            s1 = new SpriteType();  
            s2 = new SpriteType();  
            Logger::WriteMessage("Test initialized.\n"); //用于输出信息  
        }  
  
        TEST_METHOD_CLEANUP(TearDown)  
        {  
            delete s1;  
            delete s2;  
            Logger::WriteMessage("Test completed.\n");  
        }  
  
        TEST_METHOD(TestShot)  
        {  
            s1->SetValue(2.0f, 3.0f, 1.2f, 1.5f);  
            s2->SetValue(2.2f, 3.1f, 0.0f, 0.0f);  
            Assert::AreEqual<int>(1, s1->IsShotBy(s2));  
            Logger::WriteMessage("Shot tested.\n");  
        }  
    };  
}

上面 AreEqual 那句中,模板填入待测值的类型,第一个参数为预测值,第二个为实际运行的结果

,若相等则测试成功,否则为失败。写好代码后选择生成 stgTest 项目,然后在测试,窗口中打开

测试资源管理器,如果生成没有问题就可以在这里看到测试项了,选择运行即可。

既然学会了如何使用单元测试,那么以后写代码就不要忘了用哦!这样你的代码中的BUG就会越

来越少了!
========

轻松编写 C++ 单元测试

http://www.ibm.com/developerworks/cn/linux/l-cn-cppunittest/
介绍全新单元测试框架组合: googletest 与 googlemock
googletest 与 googlemock 是 Google 公司于 2008 年发布的两套用于单元测试的应用框架,本

文将向读者介绍如何应用这两套应用框架轻松编写 C++ 单元测试代码。以下讨论基于 gtest-

1.2.1 及 gmock-1.0.0 。

单元测试概述
测试并不只是测试工程师的责任,对于开发工程师,为了保证发布给测试环节的代码具有足够好的

质量( Quality ),为所编写的功能代码编写适量的单元测试是十分必要的。
单元测试( Unit Test ,模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的

、很明确的功能是否正确,通过编写单元测试可以在编码阶段发现程序编码错误,甚至是程序设计

错误。
单元测试不但可以增加开发者对于所完成代码的自信,同时,好的单元测试用例往往可以在回归测

试的过程中,很好地保证之前所发生的修改没有破坏已有的程序逻辑。因此,单元测试不但不会成

为开发者的负担,反而可以在保证开发质量的情况下,加速迭代开发的过程。
对于单元测试框架,目前最为大家所熟知的是 JUnit 及其针对各语言的衍生产品, C++ 语言所对

应的 JUnit 系单元测试框架就是 CppUnit 。但是由于 CppUnit 的设计严格继承自 JUnit ,而没

有充分考虑 C++ 与 Java 固有的差异(主要是由于 C++ 没有反射机制,而这是 JUnit 设计的基

础),在 C++ 中使用 CppUnit 进行单元测试显得十分繁琐,这一定程度上制约了 CppUnit 的普

及。笔者在这里要跟大家介绍的是一套由 google 发布的开源单元测试框架( Testing

Framework ): googletest 。

应用 googletest 编写单元测试代码
googletest 是由 Google 公司发布,且遵循 New BSD License (可用作商业用途)的开源项目

,并且 googletest 可以支持绝大多数大家所熟知的平台。与 CppUnit 不同的是: googletest 可

以自动记录下所有定义好的测试,不需要用户通过列举来指明哪些测试需要运行。
定义单元测试
在应用 googletest 编写单元测试时,使用 TEST() 宏来声明测试函数。如:
清单 1. 用 TEST() 宏声明测试函数
TEST(GlobalConfigurationTest, configurationDataTest) 
 TEST(GlobalConfigurationTest, noConfigureFileTest)
分别针对同一程序单元 GlobalConfiguration 声明了两个不同的测试(Test)函数,以分别对配

置数据进行检查( configurationDataTest ),以及测试没有配置文件的特殊情况(

noConfigureFileTest )。
实现单元测试
针对同一程序单元设计出不同的测试场景后(即划分出不同的 Test 后),开发者就可以编写单元

测试分别实现这些测试场景了。
在 googletest 中实现单元测试,可通过 ASSERT_* 和 EXPECT_* 断言来对程序运行结果进行检查

。 ASSERT_* 版本的断言失败时会产生致命失败,并结束当前函数; EXPECT_* 版本的断言失败

时产生非致命失败,但不会中止当前函数。因此, ASSERT_* 常常被用于后续测试逻辑强制依赖

的处理结果的断言,如创建对象后检查指针是否为空,若为空,则后续对象方法调用会失败;而

EXPECT_* 则用于即使失败也不会影响后续测试逻辑的处理结果的断言,如某个方法返回结果的多

个属性的检查。
googletest 中定义了如下的断言:
表 1: googletest 定义的断言( Assert )
基本断言 二进制比较 字符串比较
ASSERT_TRUE(condition);
EXPECT_TRUE(condition);
condition为真
ASSERT_FALSE(condition);
EXPECT_FALSE(condition);
condition为假 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 ASSERT_STREQ(expected_str,actual_str);
EXPECT_STREQ(expected_str,actual_str);
两个 C 字符串有相同的内容
ASSERT_STRNE(str1,str2);
EXPECT_STRNE(str1,str2);
两个 C 字符串有不同的内容
ASSERT_STRCASEEQ(expected_str,actual_str);
EXPECT_STRCASEEQ(expected_str,actual_str);
两个 C 字符串有相同的内容,忽略大小写
ASSERT_STRCASENE(str1,str2);
EXPECT_STRCASENE(str1,str2);
两个 C 字符串有不同的内容,忽略大小写
下面的实例演示了上面部分断言的使用:
清单 2. 一个较完整的 googletest 单元测试实例
// Configure.h 
 #pragma once

#include <string> 
 #include <vector>

class Configure 
 { 
 private: 
    std::vector<std::string> vItems;

public: 
    int addItem(std::string str);

std::string getItem(int index);

int getSize(); 
 };

// Configure.cpp 
 #include "Configure.h"

#include <algorithm>

/** 
 * @brief Add an item to configuration store. Duplicate item will be ignored 
 * @param str item to be stored 
 * @return the index of added configuration item 
 */ 
 int Configure::addItem(std::string str) 
 { 
std::vector<std::string>::const_iterator vi=std::find(vItems.begin(), vItems.end(), str); 
    if (vi != vItems.end()) 
        return vi - vItems.begin();

vItems.push_back(str); 
    return vItems.size() - 1; 
 }

/** 
 * @brief Return the configure item at specified index. 
 * If the index is out of range, "" will be returned 
 * @param index the index of item 
 * @return the item at specified index 
 */ 
 std::string Configure::getItem(int index) 
 { 
    if (index >= vItems.size()) 
        return ""; 
    else 
        return vItems.at(index); 
 }

/// Retrieve the information about how many configuration items we have had 
 int Configure::getSize() 
 { 
    return vItems.size(); 
 }

// ConfigureTest.cpp 
 #include <gtest/gtest.h>

#include "Configure.h"

TEST(ConfigureTest, addItem) 
 { 
    // do some initialization 
    Configure* pc = new Configure(); 
    
    // validate the pointer is not null 
    ASSERT_TRUE(pc != NULL);

// call the method we want to test 
    pc->addItem("A"); 
    pc->addItem("B"); 
    pc->addItem("A");

// validate the result after operation 
    EXPECT_EQ(pc->getSize(), 2); 
    EXPECT_STREQ(pc->getItem(0).c_str(), "A"); 
    EXPECT_STREQ(pc->getItem(1).c_str(), "B"); 
    EXPECT_STREQ(pc->getItem(10).c_str(), "");

delete pc; 
 }
运行单元测试
在实现完单元测试的测试逻辑后,可以通过 RUN_ALL_TESTS() 来运行它们,如果所有测试成功,

该函数返回 0,否则会返回 1 。 RUN_ALL_TESTS() 会运行你链接到的所有测试――它们可以来自不

同的测试案例,甚至是来自不同的文件。
因此,运行 googletest 编写的单元测试的一种比较简单可行的方法是:
为每一个被测试的 class 分别创建一个测试文件,并在该文件中编写针对这一 class 的单元测试;
编写一个 Main.cpp 文件,并在其中包含以下代码,以运行所有单元测试:
清单 3. 初始化 googletest 并运行所有测试
#include <gtest/gtest.h>

int main(int argc, char** argv) { 
    testing::InitGoogleTest(&argc, argv);

// Runs all tests using Google Test. 
    return RUN_ALL_TESTS(); 
 }
最后,将所有测试代码及 Main.cpp 编译并链接到目标程序中。
此外,在运行可执行目标程序时,可以使用 --gtest_filter 来指定要执行的测试用例,如:
./foo_test 没有指定filter,运行所有测试;
./foo_test --gtest_filter=* 指定filter为*,运行所有测试;
./foo_test --gtest_filter=FooTest.* 运行测试用例FooTest的所有测试;
./foo_test --gtest_filter=*Null*:*Constructor* 运行所有全名(即测试用例名 + “ . ” + 测试名

,如 GlobalConfigurationTest.noConfigureFileTest)含有"Null"或"Constructor"的测试;
./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行测试用例FooTest的所有测试,但不包括

FooTest.Bar。
这一特性在包含大量测试用例的项目中会十分有用。
回页首
应用 googlemock 编写 Mock Objects
很多 C++ 程序员对于 Mock Objects (模拟对象)可能比较陌生,模拟对象主要用于模拟整个应

用程序的一部分。在单元测试用例编写过程中,常常需要编写模拟对象来隔离被测试单元的“下游

”或“上游”程序逻辑或环境,从而达到对需要测试的部分进行隔离测试的目的。
例如,要对一个使用数据库的对象进行单元测试,安装、配置、启动数据库、运行测试,然后再卸

装数据库的方式,不但很麻烦,过于耗时,而且容易由于环境因素造成测试失败,达不到单元测试

的目的。模仿对象提供了解决这一问题的方法:模仿对象符合实际对象的接口,但只包含用来“欺

骗”测试对象并跟踪其行为的必要代码。因此,其实现往往比实际实现类简单很多。
为了配合单元测试中对 Mocking Framework 的需要, Google 开发并于 2008 年底开放了:

googlemock 。与 googletest 一样, googlemock 也是遵循 New BSD License (可用作商业

用途)的开源项目,并且 googlemock 也可以支持绝大多数大家所熟知的平台。
注 1:在 Windows 平台上编译 googlemock
对于 Linux 平台开发者而言,编译 googlemock 可能不会遇到什么麻烦;但是对于 Windows 平

台的开发者,由于 Visual Studio 还没有提供 tuple ( C++0x TR1 中新增的数据类型)的实现,

编译 googlemock 需要为其指定一个 tuple 类型的实现。著名的开源 C++ 程序库 boost 已经提

供了 tr1 的实现,因此,在 Windows 平台下可以使用 boost 来编译 googlemock 。为此,需要

修改 %GMOCK_DIR%/msvc/gmock_config.vsprops ,设定其中 BoostDir 到 boost 所在的目

录,如:
<UserMacro 
    Name="BoostDir" 
    Value="$(BOOST_DIR)" 
 />
其中 BOOST_DIR 是一个环境变量,其值为 boost 库解压后所在目录。
对于不希望在自己的开发环境上解包 boost 库的开发者,在 googlemock 的网站上还提供了一个

从 boost 库中单独提取出来的 tr1 的实现,可将其下载后将解压目录下的 boost 目录拷贝到

%GMOCK_DIR% 下(这种情况下,请勿修改上面的配置项;建议对 boost 不甚了解的开发者采

用后面这种方式)。
在应用 googlemock 来编写 Mock 类辅助单元测试时,需要:
编写一个 Mock Class (如 class MockTurtle ),派生自待 Mock 的抽象类(如 class Turtle )


对于原抽象类中各待 Mock 的 virtual 方法,计算出其参数个数 n ;
在 Mock Class 类中,使用 MOCK_METHODn() (对于 const 方法则需用

MOCK_CONST_METHODn() )宏来声明相应的 Mock 方法,其中第一个参数为待 Mock 方法

的方法名,第二个参数为待 Mock 方法的类型。如下:
清单 4. 使用 MOCK_METHODn 声明 Mock 方法
#include <gmock/gmock.h>  // Brings in Google Mock.

class MockTurtle : public Turtle { 
    MOCK_METHOD0(PenUp, void()); 
    MOCK_METHOD0(PenDown, void()); 
    MOCK_METHOD1(Forward, void(int distance)); 
    MOCK_METHOD1(Turn, void(int degrees)); 
    MOCK_METHOD2(GoTo, void(int x, int y)); 
    MOCK_CONST_METHOD0(GetX, int()); 
    MOCK_CONST_METHOD0(GetY, int()); 
 };
在完成上述工作后,就可以开始编写相应的单元测试用例了。在编写单元测试时,可通过

ON_CALL 宏来指定 Mock 方法被调用时的行为,或 EXPECT_CALL 宏来指定 Mock 方法被调用

的次数、被调用时需执行的操作等,并对执行结果进行检查。如下:
清单 5. 使用 ON_CALL 及 EXPECT_CALL 宏
using testing::Return;                              // #1,必要的声明

TEST(BarTest, DoesThis) { 
    MockFoo foo;                                    // #2,创建 Mock 对象

ON_CALL(foo, GetSize())                         // #3,设定 Mock 对象默认的行为(可选)
        .WillByDefault(Return(1)); 
    // ... other default actions ...

EXPECT_CALL(foo, Describe(5))                   // #4,设定期望对象被访问的方式及其响应
        .Times(3) 
        .WillRepeatedly(Return("Category 5")); 
    // ... other expectations ...

EXPECT_EQ("good", MyProductionFunction(&foo));  
    // #5,操作 Mock 对象并使用 googletest 提供的断言验证处理结果
 }                                                  
 // #6,当 Mock 对象被析构时, googlemock 会对结果进行验证以判断其行为是否与所有设定

的预期一致
其中, WillByDefault 用于指定 Mock 方法被调用时的默认行为; Return 用于指定方法被调用

时的返回值; Times 用于指定方法被调用的次数; WillRepeatedly 用于指定方法被调用时重复的

行为。
对于未通过 EXPECT_CALL 声明而被调用的方法,或不满足 EXPECT_CALL 设定条件的 Mock 方

法调用, googlemock 会输出警告信息。对于前一种情况下的警告信息,如果开发者并不关心这

些信息,可以使用 Adapter 类模板 NiceMock 避免收到这一类警告信息。如下:
清单 6. 使用 NiceMock 模板
testing::NiceMock<MockFoo> nice_foo;
在笔者开发的应用中,被测试单元会通过初始化时传入的上层应用的接口指针,产生大量的处理成

功或者失败的消息给上层应用,而开发者在编写单元测试时并不关心这些消息的内容,通过使用

NiceMock 可以避免为不关心的方法编写 Mock 代码(注意:这些方法仍需在 Mock 类中声明,

否则 Mock 类会被当作 abstract class 而无法实例化)。
与 googletest 一样,在编写完单元测试后,也需要编写一个如下的入口函数来执行所有的测试:
清单 7. 初始化 googlemock 并运行所有测试
#include <gtest/gtest.h> 
 #include <gmock/gmock.h>

int main(int argc, char** argv) { 
    testing::InitGoogleMock(&argc, argv);

// Runs all tests using Google Test. 
    return RUN_ALL_TESTS(); 
 }
下面的代码演示了如何使用 googlemock 来创建 Mock Objects 并设定其行为,从而达到对核心

类 AccountService 的 transfer (转账)方法进行单元测试的目的。由于 AccountManager 类

的具体实现涉及数据库等复杂的外部环境,不便直接使用,因此,在编写单元测试时,我们用

MockAccountManager 替换了具体的 AccountManager 实现。
清单 8. 待测试的程序逻辑
// Account.h 
 // basic application data class 
 #pragma once

#include <string>

class Account 
 { 
 private: 
    std::string accountId;

long balance;

public: 
    Account();

Account(const std::string& accountId, long initialBalance);

void debit(long amount);

void credit(long amount);

long getBalance() const;

std::string getAccountId() const; 
 };

// Account.cpp 
 #include "Account.h"

Account::Account() 
 { 
 }

Account::Account(const std::string& accountId, long initialBalance) 
 { 
    this->accountId = accountId; 
    this->balance = initialBalance; 
 }

void Account::debit(long amount) 
 { 
    this->balance -= amount; 
 }

void Account::credit(long amount) 
 { 
    this->balance += amount; 
 }

long Account::getBalance() const 
 { 
    return this->balance; 
 }

std::string Account::getAccountId() const 
 { 
    return accountId; 
 }

// AccountManager.h 
 // the interface of external services which should be mocked 
 #pragma once

#include <string>

#include "Account.h"

class AccountManager 
 { 
 public: 
    virtual Account findAccountForUser(const std::string& userId) = 0;

virtual void updateAccount(const Account& account) = 0; 
 };

// AccountService.h 
 // the class to be tested 
 #pragma once

#include <string>

#include "Account.h" 
 #include "AccountManager.h"

class AccountService 
 { 
 private: 
    AccountManager* pAccountManager;

public: 
    AccountService();

void setAccountManager(AccountManager* pManager); 
    void transfer(const std::string& senderId, 
               const std::string& beneficiaryId, long amount); 
 };

// AccountService.cpp 
 #include "AccountService.h"

AccountService::AccountService() 
 { 
    this->pAccountManager = NULL; 
 }

void AccountService::setAccountManager(AccountManager* pManager) 
 { 
    this->pAccountManager = pManager; 
 }

void AccountService::transfer(const std::string& senderId, 
                  const std::string& beneficiaryId, long amount) 
 { 
    Account sender = this->pAccountManager->findAccountForUser(senderId);

Account beneficiary = this->pAccountManager->findAccountForUser(beneficiaryId);

sender.debit(amount);

beneficiary.credit(amount);

this->pAccountManager->updateAccount(sender);

this->pAccountManager->updateAccount(beneficiary); 
 }
清单 9. 相应的单元测试
// AccountServiceTest.cpp 
 // code to test AccountService 
 #include <map> 
 #include <string>

#include <gtest/gtest.h> 
 #include <gmock/gmock.h>

#include "../Account.h" 
 #include "../AccountService.h" 
 #include "../AccountManager.h"

// MockAccountManager, mock AccountManager with googlemock 
 class MockAccountManager : public AccountManager 
 { 
 public: 
    MOCK_METHOD1(findAccountForUser, Account(const std::string&));

MOCK_METHOD1(updateAccount, void(const Account&)); 
 };

// A facility class acts as an external DB 
 class AccountHelper 
 { 
 private: 
    std::map<std::string, Account> mAccount; 
             // an internal map to store all Accounts for test

public: 
    AccountHelper(std::map<std::string, Account>& mAccount);

void updateAccount(const Account& account);

Account findAccountForUser(const std::string& userId); 
 };

AccountHelper::AccountHelper(std::map<std::string, Account>& mAccount) 
 { 
    this->mAccount = mAccount; 
 }

void AccountHelper::updateAccount(const Account& account) 
 { 
    this->mAccount[account.getAccountId()] = account; 
 }

Account AccountHelper::findAccountForUser(const std::string& userId) 
 { 
    if (this->mAccount.find(userId) != this->mAccount.end()) 
        return this->mAccount[userId]; 
    else 
        return Account(); 
 }

// Test case to test AccountService 
 TEST(AccountServiceTest, transferTest) 
 { 
    std::map<std::string, Account> mAccount; 
    mAccount["A"] = Account("A", 3000); 
    mAccount["B"] = Account("B", 2000); 
    AccountHelper helper(mAccount);

MockAccountManager* pManager = new MockAccountManager();

// specify the behavior of MockAccountManager 
    // always invoke AccountHelper::findAccountForUser 
     // when AccountManager::findAccountForUser is invoked 
    EXPECT_CALL(*pManager, findAccountForUser(testing::_)).WillRepeatedly( 
        testing::Invoke(&helper, &AccountHelper::findAccountForUser));

// always invoke AccountHelper::updateAccount 
    //when AccountManager::updateAccount is invoked 
    EXPECT_CALL(*pManager, updateAccount(testing::_)).WillRepeatedly( 
        testing::Invoke(&helper, &AccountHelper::updateAccount));

AccountService as; 
    // inject the MockAccountManager object into AccountService 
    as.setAccountManager(pManager);

// operate AccountService 
    as.transfer("A", "B", 1005);

// check the balance of Account("A") and Account("B") to 
    //verify that AccountService has done the right job 
    EXPECT_EQ(1995, helper.findAccountForUser("A").getBalance()); 
    EXPECT_EQ(3005, helper.findAccountForUser("B").getBalance());

delete pManager; 
 }

// Main.cpp 
 #include <gtest/gtest.h> 
 #include <gmock/gmock.h>

int main(int argc, char** argv) { 
    testing::InitGoogleMock(&argc, argv);

// Runs all tests using Google Test. 
    return RUN_ALL_TESTS(); 
 }
注 2:上述范例工程详见附件。要编译该工程,请读者自行添加环境变量 GTEST_DIR 、

GMOCK_DIR ,分别指向 googletest 、 googlemock 解压后所在目录;对于 Windows 开发者

,还需要将 %GMOCK_DIR%/msvc/gmock_config.vsprops 通过 View->Property Manager 添

加到工程中,并将 gmock.lib 拷贝到工程目录下。
通过上面的实例可以看出, googlemock 为开发者设定 Mock 类行为,跟踪程序运行过程及结果

,提供了丰富的支持。但与此同时,应用程序也应该尽量降低应用代码间的耦合度,使得单元测试

可以很容易对被测试单元进行隔离(如上例中, AccountService 必须提供了相应的方法以支持

AccountManager 的替换)。关于如何通过应用设计模式来降低应用代码间的耦合度,从而编写

出易于单元测试的代码,请参考本人的另一篇文章《应用设计模式编写易于单元测试的代码》(

developerWorks , 2008 年 7 月)。
注 3:此外,开发者也可以直接通过继承被测试类,修改与外围环境相关的方法的实现,达到对其

核心方法进行单元测试的目的。但由于这种方法直接改变了被测试类的行为,同时,对被测试类自

身的结构有一些要求,因此,适用范围比较小,笔者也并不推荐采用这种原始的 Mock 方式来进

行单元测试。
回页首
总结
Googletest 与 googlemock 的组合,很大程度上简化了开发者进行 C++ 应用程序单元测试的编

码工作,使得单元测试对于 C++ 开发者也可以变得十分轻松;同时, googletest 及

googlemock 目前仍在不断改进中,相信随着其不断发展,这一 C++ 单元测试的全新组合将变

得越来越成熟、越来越强大,也越来越易用。
========

C++单元测试学习总结9相关推荐

  1. Qt程序单元测试学习记录

      7月比较忙,很少更新博客,上旬任务是给公司原来的程序做简单的单元测试.毕业这两年写过很多代码,从来没有注意过单元测试这东西,现在开始认真对待,开始看别人写的文章来学习.这里记录下最近学到的,以及自 ...

  2. Android 单元测试学习计划

    网上查了一下Android单元测试相关的知识点,总结了一个学习步骤: 1. 什么是单元测试 2. 单元测试正反面: 2.1. 重要性 2.2. 缺陷 2.3. 策略 3. 单元测试的基础知识: 3.1 ...

  3. Android单元测试学习总结

    文章目录 一.本地单元测试 1. 创建测试类 2. Assert类中的常用断言方法 3. 运行测试类 4. 运行单个测试方法或多个测试类 二.Mockito测试框架的使用 1. Mock概念的理解 2 ...

  4. Go单元测试学习笔记 V1.0

    与你相识 博主介绍: – 本人是普通大学生一枚,每天钻研计算机技能,CSDN主要分享一些技术内容,因我常常去寻找资料,不经常能找到合适的,精品的,全面的内容,导致我花费了大量的时间,所以会将摸索的内容 ...

  5. GoogleTest单元测试学习

    一.GTest简介 GoogleTest是一个单元测试的框架. 单元测试: 单元测试在函数在整个工程运行起来之前,对该函数进行测试,来判断当前函数能否达到预期的效果. 使用GoogleTest: 1. ...

  6. Android单元测试学习之 Junit4

    保持单元测试的独立性,单元测试用例之间绝不能互相调用,也不能依赖执行的先后次序 Repeatable 重复性 单元测试是可以重复执行的,不能收到外界环境的影响,因为单元测试通常会被放到持续集成中,每次 ...

  7. Junit单元测试学习笔记一

    我们在编写大型程序的时候,需要写成千上万个方法或函数,这些函数的功能可能很强大,但我们在程序中只用到该函数的一小部分功能,并且经过调试可以确定,这一小部分功能是正确的.但是,我们同时应该确保每一个函数 ...

  8. testNG单元测试学习

    [demo的项目报以及流程文档放在了资源文件上] 1 TestNG的介绍 1.1 概念: TestNG(Test Next Generation)是一个测试框架,在Junit和NUnit基础上引入新功 ...

  9. Xunit.net 单元测试学习——按顺序测试

    最近一直在提高编码的质量,为了保证质量,必须有一套趁手的兵器,Xunit.Net 就是如此霸气的利器. 怎么编写单元测试 怎么会有如此低级的疑问呢?因为从接触单元测试,到使用上,真的不是一个简单的心路 ...

最新文章

  1. lattice LFE3-17EA 调试记录
  2. 1.1.2 标准化工作及相关组织
  3. 嵌入式开发试题1-100
  4. C4D合集灵感|时尚潮流色彩、搭配梦幻C4D元素设计
  5. git clone server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile
  6. [swift] LeetCode 136. Single Number
  7. android room数据库embed,Android room数据库基操
  8. Hbase ConnectionFactory
  9. 字典生成_数据字典文档自成工具,一键生成,效率倍增
  10. 阿里巴巴P3C java编程规范(最新版github下载)
  11. viper4android 6.0脉冲,【详解】v4a音效脉冲样本
  12. 用ArrayList实现简单队列和栈
  13. python和html和css什么关系什么区_Python 【第七章】:Html 和 CSS
  14. 北京航空航天大学、浙江大学等27支海内外高校队伍晋级ASC超算大赛总决赛
  15. python输出完全平方数_LeetCode 279*. 完全平方数(Python)
  16. QQ空间迁移_【深度解锁数据恢复】
  17. python发送soap报文_使用Python将带附件的XML发送到SOAP ws
  18. 静生定,定生慧,慧至从容
  19. 如何检测支付宝名字性别
  20. python end if_阅读下面的Python程序,请问输出结果是什么? print(T,end=' ') if not 0 else print('F',end=' ') print(T...

热门文章

  1. python圆的周长和面积返回2个值的元组_初见Python2:列表和元组
  2. 线程池底层工作原理?
  3. openFeign 服务接口的调用01——概述
  4. 图片标签|| 列表标签||链接标签||div和span|| 语义化标签
  5. java 接口 实现和继承关系
  6. JQuery eval函数
  7. chrome浏览器下用几行js代码实现写入剪贴板功能
  8. Excel VBA中的等价(Eqv)和蕴含(Imp)
  9. [YTU]_2575( 交通工具信息)
  10. mysql中的merge into,SQL Server 2008中利用merge into关键实现insert/update自动匹配(类似于MySQL中的For Update关键字)...