在之前一篇C++ Programming with TDD博客中,我带给大家gmock框架的简介(地址戳着里),今天我们继续本系列,带个大家C++中的单元测试框架CppUTest的介绍。

CppUTest 是一个功能全面的测试框架。CppUTest是为了支持在多种操作系统上开发嵌入式软件而特别设计的。CppUTest的宏被设计成不需要了解C++也可以写测试用例。这使得C程序员更容易用这个测试框架。CppUTest只使用C++语言中主要的那部分子集,这种选择很好地适应了那些编译器不能完全支持全部C++语言特性的嵌入式开发。你会看到用Unity和CppUTest写出的单元测试几乎一模一样。你当然可以选择任意一个测试框架来进行你的产品开发。

 一、 CppUTest的安装

CppUTest项目地址:http://www.cpputest.org/,目前的最新版本是cpputest-3.5,下载解压,然后切换到源文件目录,运行以下命令安装:

 cd $CPPUTEST_HOME
./configure
make
make -f Makefile_CppUTestExt

二、 CppUTest实例

直接贴代码:

 /*
Filename: location.h
*/
#ifndef Location_h
#define Location_h #include <limits>
#include <cmath>
#include <ostream> const double Pi{ 4.0 * atan(1.0) };
const double ToRadiansConversionFactor{ Pi / };
const double RadiusOfEarthInMeters{ };
const double MetersPerDegreeAtEquator{ }; const double North{ };
const double West{ };
const double South{ };
const double East{ };
const double CloseMeters{ }; class Location {
public:
Location();
Location(double latitude, double longitude); inline double toRadians(double degrees) const {
return degrees * ToRadiansConversionFactor;
} inline double toCoordinate(double radians) const {
return radians * ( / Pi);
} inline double latitudeAsRadians() const {
return toRadians(latitude_);
} inline double longitudeAsRadians() const {
return toRadians(longitude_);
} double latitude() const;
double longitude() const; bool operator==(const Location& that);
bool operator!=(const Location& that); Location go(double meters, double bearing) const;
double distanceInMeters(const Location& there) const;
bool isUnknown() const;
bool isVeryCloseTo(const Location& there) const; private:
double latitude_;
double longitude_; double haversineDistance(Location there) const;
}; std::ostream& operator<<(std::ostream& output, const Location& location); #endif
 /*
Filename: GeoServer.h
*/
#ifndef GeoServer_h
#define GeoServer_h #include <string>
#include <unordered_map> #include "Location.h" class GeoServer {
public:
void track(const std::string& user);
void stopTracking(const std::string& user);
void updateLocation(const std::string& user, const Location& location); bool isTracking(const std::string& user) const;
Location locationOf(const std::string& user) const; private:
std::unordered_map<std::string, Location> locations_; std::unordered_map<std::string, Location>::const_iterator
find(const std::string& user) const;
}; #endif
 /*
Filename: Location.cpp
*/
#include "Location.h" #include <ostream> using namespace std; ostream& operator<<(ostream& output, const Location& location) {
output << "(" << location.latitude() << "," << location.longitude() << ")";
return output;
} Location::Location()
: latitude_(std::numeric_limits<double>::infinity())
, longitude_(std::numeric_limits<double>::infinity()) {} Location::Location(double latitude, double longitude)
: latitude_(latitude), longitude_(longitude) {} double Location::latitude() const {
return latitude_;
} double Location::longitude() const {
return longitude_;
} bool Location::operator==(const Location& that) {
return
longitude_ == that.longitude_ &&
latitude_ == that.latitude_;
} bool Location::operator!=(const Location& that) {
return !(*this == that);
} // from williams.best.vwh.net/avform.htm#LL
Location Location::go(double meters, double bearing) const {
bearing = toRadians(bearing);
double distance { meters / RadiusOfEarthInMeters };
double newLat {
asin(sin(latitudeAsRadians()) * cos(distance) +
cos(latitudeAsRadians()) * sin(distance) * cos(bearing)) }; double newLong = longitudeAsRadians();
if (cos(latitudeAsRadians()) != )
newLong =
fmod(longitudeAsRadians() - asin(sin(bearing) * sin(distance) / cos(newLat)) + Pi,
* Pi) - Pi; return Location(toCoordinate(newLat), toCoordinate(newLong));
} double Location::distanceInMeters(const Location& there) const {
return RadiusOfEarthInMeters * haversineDistance(there);
} bool Location::isUnknown() const {
return latitude_ == std::numeric_limits<double>::infinity();
} bool Location::isVeryCloseTo(const Location& there) const {
return distanceInMeters(there) <= CloseMeters;
} double Location::haversineDistance(Location there) const {
double deltaLongitude { longitudeAsRadians() - there.longitudeAsRadians() };
double deltaLatitude { latitudeAsRadians() - there.latitudeAsRadians() }; double aHaversine {
pow(
sin(deltaLatitude / 2.0), 2.0) +
cos(latitudeAsRadians()) * cos(there.latitudeAsRadians()) * pow(sin(deltaLongitude / ),
) };
return * atan2(sqrt(aHaversine), sqrt(1.0 - aHaversine));
}
 /*
Filename: GeoServer.cpp
*/
#include "GeoServer.h"
#include "Location.h"
using namespace std;
void GeoServer::track(const string& user) {
locations_[user] = Location();
} void GeoServer::stopTracking(const string& user) {
locations_.erase(user);
} bool GeoServer::isTracking(const string& user) const {
return find(user) != locations_.end();
} void GeoServer::updateLocation(const string& user, const Location& location) {
locations_[user] = location;
}
Location GeoServer::locationOf(const string& user) const {
if (!isTracking(user)) return Location{}; // TODO performance cost?
return find(user)->second;
} std::unordered_map<std::string, Location>::const_iterator
GeoServer::find(const std::string& user) const {
return locations_.find(user);
}
 /*
Filename: LocationTest.cpp
*/
#include "CppUTest/TestHarness.h" #include <sstream> #include "Location.h" using namespace std; SimpleString StringFrom(const Location& location) {
return SimpleString(
StringFromFormat("(%d, %d)",
location.latitude(), location.longitude()));
} TEST_GROUP(ALocation) {
const double Tolerance { 0.005 };
const Location ArbitraryLocation { 38.2, -104.5 };
}; TEST(ALocation, AnswersLatitudeAndLongitude) {
Location location{, }; LONGS_EQUAL(, location.latitude());
LONGS_EQUAL(, location.longitude());
} TEST(ALocation, IsNotUnknownWhenLatitudeAndLongitudeProvided) {
Location location{, }; CHECK_FALSE(location.isUnknown());
} TEST(ALocation, IsUnknownWhenLatitudeAndLongitudeNotProvided) {
Location location; CHECK_TRUE(location.isUnknown());
} TEST(ALocation, AnswersDistanceFromAnotherInMeters) {
Location point1{ 38.017, -104.84 };
Location point2{ 38.025, -104.99 }; // verified at www.ig.utexas.edu/outreach/googleearth/latlong.html
DOUBLES_EQUAL(, point1.distanceInMeters(point2), );
} TEST(ALocation, IsNotEqualToAnotherWhenLatDiffers) {
Location point1{ , };
Location point2{ , }; CHECK_TRUE(point1 != point2);
} TEST(ALocation, IsNotEqualToAnotherWhenLongDiffers) {
Location point1{ , };
Location point2{ , }; CHECK_TRUE(point1 != point2);
} TEST(ALocation, IsNotEqualToAnotherWhenLatAndLongMatch) {
Location point1{ , };
Location point2{ , }; CHECK_TRUE(point1 == point2);
} TEST(ALocation, AnswersNewLocationGivenDistanceAndBearing) {
Location start{, }; auto newLocation = start.go(MetersPerDegreeAtEquator, East); Location expectedEnd{, };
DOUBLES_EQUAL(, newLocation.longitude(), Tolerance);
DOUBLES_EQUAL(, newLocation.latitude(), Tolerance);
} TEST(ALocation, AnswersNewLocationGivenDistanceAndBearingVerifiedByHaversine) {
double distance{ };
Location start{ , - }; auto end = start.go(distance, ); DOUBLES_EQUAL(distance, start.distanceInMeters(end), Tolerance);
} TEST(ALocation, CanBeAPole) {
Location start{ , }; auto end = start.go(MetersPerDegreeAtEquator, South); DOUBLES_EQUAL(, end.longitude(), Tolerance);
DOUBLES_EQUAL(, end.latitude(), Tolerance);
} TEST(ALocation, IsVeryCloseToAnotherWhenSmallDistanceApart) {
Location threeMetersAway { ArbitraryLocation.go(, South) }; CHECK_TRUE(ArbitraryLocation.isVeryCloseTo(threeMetersAway));
} TEST(ALocation, IsNotVeryCloseToAnotherWhenNotSmallDistanceApart) {
Location fourMetersAway { ArbitraryLocation.go(, South) }; CHECK_FALSE(ArbitraryLocation.isVeryCloseTo(fourMetersAway));
} TEST(ALocation, ProvidesPrintableRepresentation) {
Location location{-, -};
stringstream s; s << location; CHECK_EQUAL("(-32,-105)", s.str());
}
 /*
Filename: CppUTestExtensions.h
*/
#ifndef CppUTestExtensions_h
#define CppUTestExtensions_h #include <string>
#include <vector>
#include <sstream>
#include <functional> #include "CppUTest/TestHarness.h" template<typename T>
SimpleString StringFrom(const std::vector<T>& list, std::function<std::string(T)> func) {
std::stringstream stream;
for (auto each: list) {
if (stream.str().length() > ) stream << ",";
stream << func(each);
}
return SimpleString(stream.str().c_str());
} SimpleString StringFrom(const std::vector<std::string>& list); #endif
 /*
Filename: CppUTestExtensions.cpp
*/
#include "CppUTest/TestHarness.h"
#include "CppUTestExtensions.h" TEST_GROUP(StringFrom_ForAVector) {
}; TEST(StringFrom_ForAVector, AnswersEmptyStringWhenVectorEmpty) {
std::vector<std::string> strings {}; CHECK_EQUAL("", StringFrom(strings));
} TEST(StringFrom_ForAVector, AnswersCommaSeparatedList) {
std::vector<std::string> strings {"alpha", "beta", "gamma"}; CHECK_EQUAL("alpha,beta,gamma", StringFrom(strings));
} struct TestItem {
TestItem(int number) : Number(number) {}
int Number;
}; TEST(StringFrom_ForAVector, AcceptsTransformLambdaSoYouCanBuildYourOwnEasily) {
std::vector<TestItem> items { TestItem(), TestItem(), TestItem() }; auto string = StringFrom<TestItem>(items,
[](TestItem item) { return std::to_string(item.Number); }); CHECK_EQUAL("1,2,3", string);
}
 /*
Filename: GeoServerTest.cpp
*/
#include "CppUTest/TestHarness.h"
#include "CppUTestExtensions.h"
#include "GeoServer.h" using namespace std; TEST_GROUP(AGeoServer) {
GeoServer server; const string aUser{"auser"};
const double LocationTolerance{0.005};
};
TEST(AGeoServer, TracksAUser) {
server.track(aUser); CHECK_TRUE(server.isTracking(aUser));
} TEST(AGeoServer, IsNotTrackingAUserNotTracked) {
CHECK_FALSE(server.isTracking(aUser));
} TEST(AGeoServer, TracksMultipleUsers) {
server.track(aUser);
server.track("anotheruser"); CHECK_FALSE(server.isTracking("thirduser"));
CHECK_TRUE(server.isTracking(aUser));
CHECK_TRUE(server.isTracking("anotheruser"));
} TEST(AGeoServer, IsTrackingAnswersFalseWhenUserNoLongerTracked) {
server.track(aUser);
server.stopTracking(aUser); CHECK_FALSE(server.isTracking(aUser));
} TEST(AGeoServer, UpdatesLocationOfUser) {
server.track(aUser);
server.updateLocation(aUser, Location{, -}); auto location = server.locationOf(aUser);
DOUBLES_EQUAL(, location.latitude(), LocationTolerance);
DOUBLES_EQUAL(-, location.longitude(), LocationTolerance);
} TEST(AGeoServer, AnswersUnknownLocationForUserNotTracked) {
CHECK_TRUE(server.locationOf("anAbUser").isUnknown());
} TEST(AGeoServer, AnswersUnknownLocationForTrackedUserWithNoLocationUpdate) {
server.track(aUser);
CHECK_TRUE(server.locationOf(aUser).isUnknown());
} TEST(AGeoServer, AnswersUnknownLocationForUserNoLongerTracked) {
server.track(aUser);
server.updateLocation(aUser, Location(, ));
server.stopTracking(aUser);
CHECK_TRUE(server.locationOf(aUser).isUnknown());
}

好了,开始我们的testmain函数吧:

 #include "CppUTest/CommandLineTestRunner.h"

 int main(int argc, char** argv) {
return CommandLineTestRunner::RunAllTests(argc, argv);
}

全部的代码都已经好了,准备编译程序吧,为了方便编译,提供cmake文件:

 project(Extras)
cmake_minimum_required(VERSION 2.6) include_directories($ENV{CPPUTEST_HOME}/include)
link_directories($ENV{CPPUTEST_HOME}/lib) add_definitions(-g -std=c++0x) set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall")
set(sources
GeoServer.cpp
Location.cpp)
set(testSources
CppUTestExtensions.cpp
CppUTestExtensionsTest.cpp
GeoServerTest.cpp
LocationTest.cpp)
add_executable(utest testmain.cpp ${testSources} ${sources}) target_link_libraries(utest CppUTest)

编译程序,运行结果如下图:

 三、 程序分析及小结

做测试的时候,需要建立一个TEST_GROUP和TEST方法,TEST_GROUP的内部定义自己测试中需要用到的变量和一些自己的函数(变量和函数只有定义在这个里面,属于这一组的测试才能使用这些变量和函数),而且在TEST_GROUP中还可以继承两个CppUTest的函数:

 void setup(){}    //这个函数中对变量进行初始化
void teardown(){}  //对一些变量进行销毁

TEST部分中就填入我们想要做的测试用例,CppUTest提供了很多的宏,如CHECK(bool),LONGS_EQUAL(excepted,actual)…等等宏,就行一些检测,而不需要去关心C++语言的类的那些问题,所以CppUTest也可以用于C语言。

CppUTest的关键设计之一就是容易添加和删除测试。想要运行测试,main函数是不可或缺的。

感谢大家阅读!

C++ Programming with TDD之二:CppUTest单元测试的更多相关文章

  1. Visual Studio 单元测试之二---顺序单元测试

    原文:Visual Studio 单元测试之二---顺序单元测试 此文是上一篇博文:Visual Studio 单元测试之一---普通单元测试的后续篇章.如果读者对Visual Studio的单元测试 ...

  2. TDD学习笔记【二】---单元测试简介

    大纲 Testing 的第一个切入点:单元测试. 本篇文章将针对单元测试进行简介,主要内容包含了5W: Why What Where Who When 而How 的部分,属于实现部分,将于下一篇文章介 ...

  3. CPPUTest 单元测试框架(针对 C 单元测试的使用说明)

    CPPUTest 虽然名称上看起来是 C++ 的单元测试框架, 其实它也是支持测试 C 代码的. 本文主要介绍用CPPUTest来测试 C 代码. (C++没用过, 平时主要用的是C) C++相关的内 ...

  4. Spring Boot(十二)单元测试JUnit

    一.介绍 JUnit是一款优秀的开源Java单元测试框架,也是目前使用率最高最流行的测试框架,开发工具Eclipse和IDEA对JUnit都有很好的支持,JUnit主要用于白盒测试和回归测试. 白盒测 ...

  5. C++ Programming with TDD之一:GMOCK框架简介

    所谓测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法.就是在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测 ...

  6. TDD尝试:nodejs单元测试

    单元测试是最小化的测试方式,也是TDD的做法. TDD概念如下图: 通过测试反馈推进开发,ruby是推崇这种编程方式的. nodejs有如下常用单元测试模块 1.mocha Mocha是一个基于nod ...

  7. Spring Boot 系列(二)单元测试&网络请求

    实际开发中,Junit单元测试是必不可少的.在spring-boot 中可以通过测试模块(spring-boot-starter-test)快速使用单元测试功能. 开始 本示例在 spring boo ...

  8. javaScript设计模式之面向对象编程(object-oriented programming,OOP)(二)

    接上一篇 面向对象编程的理解? 答:面向对象编程,就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法).这个对象我们称之为类.面向对象编程思想其中一个特点就是封装,就是把你需 ...

  9. unittest框架(二)单元测试及测试报告

    如果要自测代码,可使用单元测试,需要导入unittest模块,import unittest即可. 例如,自测一个计算连个数相除的函数,代码如下: import unittest def calc(a ...

随机推荐

  1. [windows]清除访问共享的用户和密码信息

    方法一: 操作步骤:进入cmd命令界面-->输入:net use(查看列表)-->输入:net use * /delete(清空列表)-->输入:y 回车确认即可. [查看已记录的登 ...

  2. JavaScript对象属性

    JavaScript对象的属性有两类:数据属性和访问器属性 数据属性 数据属性有四个特性,分别为: [[value]]属性的值 [[writeable]]属性是否可以修改 [[enumerable]] ...

  3. 这些O2O比你们更靠谱儿

    本文纯属虚构,如有雷同,全是 C2C(Copy to China). 一 「什么社区 O2O,不就是跑腿儿的?那叮*小区不好好跑腿儿,非要搞什么狗屁社交,不是死了?」 三十四岁的老刘咽了口唾沫,接着跟 ...

  4. Android(java)学习笔记120:BroadcastReceiver之 应用程序安装和卸载 的广播接收者

    国内的主流网络公司(比如网易.腾讯.百度等等),他们往往采用数据挖掘技术获取用户使用信息,从而采用靶向营销.比如电脑上,我们浏览网页的时候,往往会发现网页上会出现我们之前经常浏览内容的商业广告,这就是 ...

  5. Angular - angularjs2 一些报错的概览(数据为json格式)

    {"Unterminated string literal.": "未终止的字符串文本.","Identifier expected.": ...

  6. logback写日志

    https://blog.csdn.net/u010128608/article/details/76618263 https://blog.csdn.net/zhuyucheng123/articl ...

  7. CentOS7安装配置VSFTP

    #是否开启匿名用户,匿名都不安全,不要开 anonymous_enable=NO #允许本机账号登录FTP local_enable=YES #允许账号都有写操作 write_enable=YES # ...

  8. H5(一)H5与HTML、XHTML的不同

    一.基本概念 html:超文本标记语言 (Hyper Text Markup Language) xhtml:可扩展超文本标记语言,是一种置标语言,表现方式与超文本标记语言(HTML)类似,不过语法上 ...

  9. 如何用纯 CSS 创作一台拍立得照相机

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/YjYgey 可交互视频 此视频是可 ...

  10. 用Python设置matplotlib.plot的坐标轴刻度间隔以及刻度范围

    一.用默认设置绘制折线图 import matplotlib.pyplot as plt x_values=list(range(11)) #x轴的数字是0到10这11个整数 y_values=[x* ...