在之前一篇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. 巧用代理设计模式(Proxy Design Pattern)改善前端图片加载体验

    这篇文章介绍一种使用代理设计模式(Proxy Design Pattern)的方法来改善您的前端应用里图片加载的体验. 假设我们的应用里需要显示一张尺寸很大的图片,位于远端服务器.我们用一些前端框架的 ...

  2. Array - Merge Sorted Array

    /** * 将nums2中的值合并入nums1,使其仍然有序 * 可以任务nums1的长度>=m+n * @param nums1 已排序数组 * @param m nums1数组已初始化的数目 ...

  3. 巧用 Odoo act_window 的 flags实现一些个性化的视图控制

    转自:http://www.khcloud.net:4082/?thread-58.htm 'flags': { 'sidebar': False, //是否显示sidebar区域(主要为action ...

  4. 共享服务-FTP基础(二)

    续接上一篇 使用pam(Pluggable Authentication Modules)完成用户认证 pam_service_name=vsftpd pam配置文件:/etc/pam.d/vsftp ...

  5. 01、Linux介绍

    一. Linux介绍 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的UNIX工具软件.应用程序和 ...

  6. init0-6(启动级别)

    一. init是Linux系统操作中不可缺少的程序之一. 所谓的init进程,它是一个由内核启动的用户级进程. 内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后, ...

  7. Linux redis服务搭建记录

    Redis的安装 1.安装redis需要C语言的编译环境 //gcc在线安装 yum install gcc-c++ 如果提示 /var/run/yum.pid 已被锁定,解决办法,删除yum.pid ...

  8. python入门:输出1-100之内的所有奇数和偶数

    #!/usr/bin/env python # -*- coding:utf-8 -*- #输出1-100之内的所有奇数和偶数 """ 给start赋值等于1,while ...

  9. linux三剑客正则表达式

    ^:以...开头,^d,意思是以d开头.例如:ls  -F(-p) | grep " ^d " $:以...结尾,/$,意思是以/结尾.例如:ls -F(-p) | grep &q ...

  10. FastJsonUtils工具类

    fastjson是由alibaba开源的一套json处理器.与其他json处理器(如Gson,Jackson等)和其他的Java对象序列化反序列化方式相比,有比较明显的性能优势. 版权声明:本文为博主 ...