IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> GTest使用笔记 -> 正文阅读

[开发测试]GTest使用笔记

1. C++测试框架

1.1. cppunit

cppunit,这是一个与JUnit类似的框架,这个框架很陈旧了,并且有着一些缺点,例如一些类可以消失,一些类名应该修改,一些宏定义应该修改,帮助很少很乱等,这里就不再一一赘述。

1.2. cxxtest

因为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。

1.3. GTest

gtest是一个跨平台(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)的C++测试框架,有google公司发布。gtest测试框架是在不同平台上为编写C++测试而生成的。

这是Google的开源C++单元测试框架,是遵循 New BSD License (可用作商业用途)的开源项目。据说google内部的大多数C++代码都已经使用这个测试框架进行单测,gtest 可以支持绝大多数大家所熟知的平台。

2. 安装GTest

下载源码

git clone https://github.com/google/googletest

3. 使用GTest

3.1. 代码

gtest_test.cpp文件

注意:如果有命令空间,main函数要放到命名空间外面,这里也不建议使用main函数

#include "gtest/gtest.h"
#include "fun.h"
 
 
TEST(fun, add) {
  EXPECT_EQ(1, add(2,-1));
 
  EXPECT_EQ(5, add(2,3));
}
 
int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
 
  return RUN_ALL_TESTS();
}

?fun.h文件

#ifndef _FUN_H_
#define _FUN_H_
 
#include <string.h>
#include <algorithm>
 
// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n);
 
// Returns true if n is a prime number.
bool IsPrime(int n);
 
// A simple string class.
class MyString {
private:
	const char* c_string_;
	const MyString& operator=(const MyString& rhs);
 
public:
	// Clones a 0-terminated C string, allocating memory using new.
	static const char* CloneCString(const char* a_c_string);
 
	// C'tors
 
	// The default c'tor constructs a NULL string.
	MyString() : c_string_(NULL) {}
 
	// Constructs a MyString by cloning a 0-terminated C string.
	explicit MyString(const char* a_c_string) : c_string_(NULL) {
		Set(a_c_string);
	}
 
	// Copy c'tor
	MyString(const MyString& string) : c_string_(NULL) {
		Set(string.c_string_);
	}
 
	// D'tor.  MyString is intended to be a final class, so the d'tor
	// doesn't need to be virtual.
	~MyString() { delete[] c_string_; }
 
	// Gets the 0-terminated C string this MyString object represents.
	const char* c_string() const { return c_string_; }
 
	size_t Length() const {
		return c_string_ == NULL ? 0 : strlen(c_string_);
	}
 
	// Sets the 0-terminated C string this MyString object represents.
	void Set(const char* c_string);
};
 
// Queue is a simple queue implemented as a singled-linked list.
//
// The element type must support copy constructor.
template <typename E>  // E is the element type
class Queue;
 
// QueueNode is a node in a Queue, which consists of an element of
// type E and a pointer to the next node.
template <typename E>  // E is the element type
class QueueNode {
	friend class Queue<E>;
 
public:
	// Gets the element in this node.
	const E& element() const { return element_; }
 
	// Gets the next node in the queue.
	QueueNode* next() { return next_; }
	const QueueNode* next() const { return next_; }
 
private:
	// Creates a node with a given element value.  The next pointer is
	// set to NULL.
	explicit QueueNode(const E& an_element) : element_(an_element), next_(NULL) {}
 
	// We disable the default assignment operator and copy c'tor.
	const QueueNode& operator = (const QueueNode&);
	QueueNode(const QueueNode&);
 
	E element_;
	QueueNode* next_;
};
 
template <typename E>  // E is the element type.
class Queue {
public:
	// Creates an empty queue.
	Queue() : head_(NULL), last_(NULL), size_(0) {}
 
	// D'tor.  Clears the queue.
	~Queue() { Clear(); }
 
	// Clears the queue.
	void Clear() {
		if (size_ > 0) {
			// 1. Deletes every node.
			QueueNode<E>* node = head_;
			QueueNode<E>* next = node->next();
			for (; ;) {
				delete node;
				node = next;
				if (node == NULL) break;
				next = node->next();
			}
 
			// 2. Resets the member variables.
			head_ = last_ = NULL;
			size_ = 0;
		}
	}
 
	// Gets the number of elements.
	size_t Size() const { return size_; }
 
	// Gets the first element of the queue, or NULL if the queue is empty.
	QueueNode<E>* Head() { return head_; }
	const QueueNode<E>* Head() const { return head_; }
 
	// Gets the last element of the queue, or NULL if the queue is empty.
	QueueNode<E>* Last() { return last_; }
	const QueueNode<E>* Last() const { return last_; }
 
	// Adds an element to the end of the queue.  A copy of the element is
	// created using the copy constructor, and then stored in the queue.
	// Changes made to the element in the queue doesn't affect the source
	// object, and vice versa.
	void Enqueue(const E& element) {
		QueueNode<E>* new_node = new QueueNode<E>(element);
 
		if (size_ == 0) {
			head_ = last_ = new_node;
			size_ = 1;
		} else {
			last_->next_ = new_node;
			last_ = new_node;
			size_++;
		}
	}
 
	// Removes the head of the queue and returns it.  Returns NULL if
	// the queue is empty.
	E* Dequeue() {
		if (size_ == 0) {
			return NULL;
		}
 
		const QueueNode<E>* const old_head = head_;
		head_ = head_->next_;
		size_--;
		if (size_ == 0) {
			last_ = NULL;
		}
 
		E* element = new E(old_head->element());
		delete old_head;
 
		return element;
	}
 
	// Applies a function/functor on each element of the queue, and
	// returns the result in a new queue.  The original queue is not
	// affected.
	template <typename F>
	Queue* Map(F function) const {
		Queue* new_queue = new Queue();
		for (const QueueNode<E>* node = head_; node != NULL; node = node->next_) {
			new_queue->Enqueue(function(node->element()));
		}
 
		return new_queue;
	}
 
private:
	QueueNode<E>* head_;  // The first node of the queue.
	QueueNode<E>* last_;  // The last node of the queue.
	size_t size_;  // The number of elements in the queue.
 
	// We disallow copying a queue.
	Queue(const Queue&);
	const Queue& operator = (const Queue&);
};
 
// A simple monotonic counter.
class Counter {
private:
	int counter_;
 
public:
	// Creates a counter that starts at 0.
	Counter() : counter_(0) {}
 
	// Returns the current counter value, and increments it.
	int Increment();
 
	// Prints the current counter value to STDOUT.
	void Print() const;
};
 
// The prime table interface.
class PrimeTable {
public:
	virtual ~PrimeTable() {}
 
	// Returns true iff n is a prime number.
	virtual bool IsPrime(int n) const = 0;
 
	// Returns the smallest prime number greater than p; or returns -1
	// if the next prime is beyond the capacity of the table.
	virtual int GetNextPrime(int p) const = 0;
};
 
// Implementation #1 calculates the primes on-the-fly.
class OnTheFlyPrimeTable : public PrimeTable {
public:
	virtual bool IsPrime(int n) const {
		if (n <= 1) return false;
 
		for (int i = 2; i*i <= n; i++) {
			// n is divisible by an integer other than 1 and itself.
			if ((n % i) == 0) return false;
		}
 
		return true;
	}
 
	virtual int GetNextPrime(int p) const {
		for (int n = p + 1; n > 0; n++) {
			if (IsPrime(n)) return n;
		}
 
		return -1;
	}
};
 
// Implementation #2 pre-calculates the primes and stores the result
// in an array.
class PreCalculatedPrimeTable : public PrimeTable {
public:
	// 'max' specifies the maximum number the prime table holds.
	explicit PreCalculatedPrimeTable(int max)
		: is_prime_size_(max + 1), is_prime_(new bool[max + 1]) {
			CalculatePrimesUpTo(max);
	}
	virtual ~PreCalculatedPrimeTable() { delete[] is_prime_; }
 
	virtual bool IsPrime(int n) const {
		return 0 <= n && n < is_prime_size_ && is_prime_[n];
	}
 
	virtual int GetNextPrime(int p) const {
		for (int n = p + 1; n < is_prime_size_; n++) {
			if (is_prime_[n]) return n;
		}
 
		return -1;
	}
 
private:
	void CalculatePrimesUpTo(int max) {
		::std::fill(is_prime_, is_prime_ + is_prime_size_, true);
		is_prime_[0] = is_prime_[1] = false;
 
		for (int i = 2; i <= max; i++) {
			if (!is_prime_[i]) continue;
 
			// Marks all multiples of i (except i itself) as non-prime.
			for (int j = 2*i; j <= max; j += i) {
				is_prime_[j] = false;
			}
		}
	}
 
	const int is_prime_size_;
	bool* const is_prime_;
 
	// Disables compiler warning "assignment operator could not be generated."
	void operator=(const PreCalculatedPrimeTable& rhs);
};
 
#endif//_FUN_H_

3.2. 编译

如果test中写了main函数,则编译比较简单;如果test中没有写main函数,则编译时需要链接gtest_main

3.3. 运行

make test CTEST_OUTPUT_ON_FAILURE=TRUE GTEST_COLOR=TRUE

4. GTest的一些基本概念

要测试一个类或函数,我们需要对其行为做出断言。当一个断言失败时,Google Test会在屏幕上输出该代码所在的源文件及其所在的位置行号,以及错误信息。也可以在编写断言时,提供一个自定义的错误信息,这个信息在失败时会被附加在Google Test的错误信息之后。

断言常常成对出现,它们都测试同一个类或者函数,但对当前功能有着不同的效果。ASSERT_*版本的断言失败时会产生致命失败,并结束当前函数。EXPECT_*版本的断言产生非致命失败,而不会中止当前函数。通常更推荐使用EXPECT_*断言,因为它们运行一个测试中可以有不止一个的错误被报告出来。但如果在编写断言如果失败,就没有必要继续往下执行的测试时,你应该使用ASSERT_*断言。 因为失败的ASSERT_*断言会立刻从当前的函数返回,可能会跳过其后的一些的清洁代码,这样也许会导致空间泄漏。

5. GTest的断言

5.1. 布尔值检查

Fatal assertion

Nonfatal assertion

Verifies

ASSERT_TRUE(condition);

EXPECT_TRUE(condition);

condition is true

ASSERT_FALSE(condition);

EXPECT_FALSE(condition);

condition is false

5.2. 数值型数据检查

Fatal assertion

Nonfatal assertion

Verifies

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

5.3. 字符串比较

Fatal assertion

Nonfatal assertion

Verifies

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字符串有不同的内容,忽略大小写

5.4. 异常检查

Fatal assertion

Nonfatal assertion

Verifies

ASSERT_THROW(statement, exception_type);

EXPECT_THROW(statement, exception_type);

statement throws an exception of the given type

ASSERT_ANY_THROW(statement);

EXPECT_ANY_THROW(statement);

statement throws an exception of any type

ASSERT_NO_THROW(statement);

EXPECT_NO_THROW(statement);

statement doesn't throw any exception

5.5. 浮点型检查

Fatal assertion

Nonfatal assertion

Verifies

ASSERT_FLOAT_EQ(expected, actual);

EXPECT_FLOAT_EQ(expected, actual);

the two float values are almost equal

ASSERT_DOUBLE_EQ(expected, actual);

EXPECT_DOUBLE_EQ(expected, actual);

the two double values are almost equal

?对相近的两个数比较:

Fatal assertion

Nonfatal assertion

Verifies

ASSERT_NEAR(val1, val2, abs_error);

EXPECT_NEAR(val1, val2, abs_error);

the difference between val1 and val2 doesn't exceed the given absolute error

5.6. 此外还有类型检查、谓词检查等

6. 注意事项

6.1. 生成链接库时不要编UT文件

很多工程都包含UT文件,当这个工程编译链接库时,注意要把UT文件从链接库中剔除,然后单独把UT文件编成可执行文件。

6.2. 不建议使用main函数

如果链接了gtest_main,则不需要写main函数,建议使用这种方式。

参考文献

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-11-09 19:53:42  更:2021-11-09 19:54:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 2:23:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码