单元测试从0到1学习gtestCLion
单元测试:从0到1学习gtest(CLion)
- gtest是一个跨平台的(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)C++单元测试框架,由google公司发布。gtest是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。
- 单元测试(Unit Test)是对软件基本组成单元(如函数或是一个类的方法)进行的测试,是开发者编写的一小段代码,用于检验被测试代码一个很小的、很明确的功能是否正确。
- 在gtest中,一个测试用例(test case)可以包含一个或多个测试。一个测试程序可以包含多个测试用例
理论
定义测试函数
应用 googletest 编写单元测试时,使用 TEST() 宏来声明测试函数:
TEST(GlobalConfigurationTest, configurationDataTest)
断言
gtest中断言的宏可以分为两类:一类是ASSERT宏,另一类就是EXPECT宏了。
1、ASSERT_系列:如果当前点检测失败则退出当前函数
2、EXPECT_系列:如果当前点检测失败则继续往下执行
如果你对自动输出的错误信息不满意的话,也是可以通过operator«能够在失败的时候打印日志,将一些自定义的信息输出。
ASSERT_系列:
基础断言
二进制比较断言
字符串对比断言
浮点对比断言
almost euqal表示两个数只是近似相似,默认的是是指两者的差值在4ULP之内(Units in the Last Place)。我们还可以自己制定精度
使用方法是:
ASSERT_NEAR(-1.0f, -1.1f, 0.2f);
ASSERT_NEAR(2.0f, 3.0f, 1.0f);
成功失败断言
该类断言用于直接标记是否成功或者失败。可以使用SUCCEED()宏标记成功,使用FAIL()宏标记致命错误(同ASSERT_ ),ADD_FAILURE()宏标记非致命错误(同EXPECT_ )。举个例子
if (Check) {
SUCCEED();
}
else {
FAIL();
}
异常断言
void ThrowException(int n) {
switch (n) {
case 0:
throw 0;
case 1:
throw "const char*";
case 2:
throw 1.1f;
case 3:
return;
}
}
TEST(ThrowException, Check) {
EXPECT_THROW(ThrowException(0), int);
EXPECT_THROW(ThrowException(1), const char*);
ASSERT_ANY_THROW(ThrowException(2));
ASSERT_NO_THROW(ThrowException(3));
}
事件机制
gtest提供了多种事件机制,非常方便我们在案例之前或之后做一些操作。总结一下gtest的事件一共有3种:
- 全局的,所有案例执行前后。
- TestSuite级别的,在某一批案例中第一个案例前,最后一个案例执行后。
- TestCase级别的,每个TestCase前后。
全局事件
要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。
- SetUp()方法在所有案例执行前执行
- TearDown()方法在所有案例执行后执行
还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。
实践
入门
环境linux
下载gtest源码
加入工程中:
- 编写CMakeList.txt 文件
cmake_minimum_required(VERSION 3.16)
project(TestAdd2)
set(CMAKE_CXX_STANDARD 11)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/googletest/include
)
link_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/googletest
)
# 添加下级目录,生成.a文件
add_subdirectory( ./googletest)
aux_source_directory(./src SRC_LIST)
set(SOUCE_FLIES main.cpp )
add_executable(${PROJECT_NAME} ${SOUCE_FLIES} ${SRC_LIST})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
target_link_libraries(${PROJECT_NAME} gtest)
编写代码
add.h
#ifndef TESTADD2_ADD_H
#define TESTADD2_ADD_H
int add(int n1,int n2);
#endif //TESTADD2_ADD_H
add.cpp
#include "../include/add.h"
int add(int n1,int n2)
{
return n1+n2;
}
main.c
#include <iostream>
#include "add.h"
#include "gtest/gtest.h"
TEST(TestCase,test1 ){
ASSERT_EQ(12,add(4,8));
}
TEST(TestCase,test2){
EXPECT_EQ(5,add(2,3));
}
TEST(TestCase,test3){
EXPECT_EQ(3,add(1,2)); // 是否相等eque
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv); // //将命令行参数传递给gtest
return RUN_ALL_TESTS(); // //RUN_ALL_TESTS()运行所有测试案例
}
另外:你会发现即使没有mian函数,每个test都可以单独运行。 比如对于
你可以直接执行它(
ASSERT_EQ断言两个数相等
)
上面是表示通过了。如果你想故意不通过的话,比如改变测试用例
你会发现输出如下:
如果你对自动输出的出错信息不满意的话,你还可以通过操作符«将一些自定义的信息输出,通常,这对于调试或是对一些检查点的补充说明来说,非常有用!
执行如下:
测试字符串
TEST(StringCmpTest, Demo)
{
char* pszCoderZh = "CoderZh";
wchar_t* wszCoderZh = L"CoderZh";
std::string strCoderZh = "CoderZh";
std::wstring wstrCoderZh = L"CoderZh";
EXPECT_STREQ("CoderZh", pszCoderZh);
EXPECT_STREQ(L"CoderZh", wszCoderZh);
EXPECT_STRNE("CnBlogs", pszCoderZh);
EXPECT_STRNE(L"CnBlogs", wszCoderZh);
EXPECT_STRCASEEQ("coderzh", pszCoderZh);
//EXPECT_STRCASEEQ(L"coderzh", wszCoderZh); 不支持
EXPECT_STREQ("CoderZh", strCoderZh.c_str());
EXPECT_STREQ(L"CoderZh", wstrCoderZh.c_str());
}
全局事件
- main函数中
#include <iostream>
#include "gtest/gtest.h"
class FooEnvironment: public testing::Environment{
public:
virtual void SetUp(){
std::cout << "Foo FooEnvironment SetUP" << std::endl;
}
virtual void TearDown(){
std::cout << "Foo FooEnvironment TearDown" << std::endl;
}
};
TEST(StringCmpTest, Demo){ // 必须由一个testUnit才会执行事件
}
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from %s\n", __FILE__);
testing::AddGlobalTestEnvironment(new FooEnvironment);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
测试普通函数
CMakeLists.txt同上
- 第1步:编写代码
sample1.h
#ifndef GTEST_SAMPLES_SAMPLE1_H_
#define GTEST_SAMPLES_SAMPLE1_H_
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n);
// Returns true if and only if n is a prime number.
bool IsPrime(int n);
#endif // GTEST_SAMPLES_SAMPLE1_H_
sample1.cc
#include "sample1.h"
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
// Returns true if and only if n is a prime number.
bool IsPrime(int n) {
// Trivial case 1: small numbers
if (n <= 1) return false;
// Trivial case 2: even numbers
if (n % 2 == 0) return n == 2;
// Now, we have that n is odd and n >= 3.
// Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2) {
// We only have to try i up to the square root of n
if (i > n/i) break;
// Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0) return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
第2步:编写测试用例
sample1_unittest.cc
#include <limits.h>
#include "sample1.h"
#include "gtest/gtest.h"
namespace {
TEST(FactorialTest, Negative) {
// This test is named "Negative", and belongs to the "FactorialTest"
// test case.
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1)); //equal才返回true
EXPECT_GT(Factorial(-10), 0); // 大于才返回true
}
// Tests factorial of 0.
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0));
}
// Tests factorial of positive numbers.
TEST(FactorialTest, Positive) {
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// -------------------------------------------------------------
// Tests negative input.
TEST(IsPrimeTest, Negative) {
// This test belongs to the IsPrimeTest test case.
EXPECT_FALSE(IsPrime(-1)); //期待结果是false
EXPECT_FALSE(IsPrime(-2)); //期待结果是false
EXPECT_FALSE(IsPrime(INT_MIN)); //期待结果是false
}
// Tests some trivial cases.
TEST(IsPrimeTest, Trivial) {
EXPECT_FALSE(IsPrime(0)); // 期待结果是false
EXPECT_FALSE(IsPrime(1)); //期待结果是false
EXPECT_TRUE(IsPrime(2)); // 期待结果是true
EXPECT_TRUE(IsPrime(3));// 期待结果是true
}
// Tests positive input.
TEST(IsPrimeTest, Positive) {
EXPECT_FALSE(IsPrime(4)); //期待结果是false
EXPECT_TRUE(IsPrime(5)); // 期待结果是true
EXPECT_FALSE(IsPrime(6)); //期待结果是false
EXPECT_TRUE(IsPrime(23)); // 期待结果是true
}
} // namespace
- 第3步:在main()中调用RUN_ALL_TESTS()
#include <iostream>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
第3个例子:类具有多个成员函数
项目结构与第一个例子相同
除了编写代码部分和编写测试用例部分
demo1
- 第1步:编写代码
sample1.h
#pragma once
class Counter{
private:
int counter_;
public:
Counter():counter_(0){}
int Increment();
int Decrement();
void Print() const;
};
sample1.cc
#include "sample1.h"
#include <cstdio>
int Counter::Increment() {
return counter_++;
}
int Counter::Decrement() {
if (counter_ == 0) {
return counter_;
} else {
return counter_--;
}
}
void Counter::Print() const {
printf("%d", counter_);
}
- 编写测试用例
#include "sample1.h"
#include "gtest/gtest.h"
namespace {
TEST(Connter, Increment){
Counter c;
EXPECT_EQ(0, c.Decrement());
EXPECT_EQ(0, c.Increment());
EXPECT_EQ(1, c.Increment());
EXPECT_EQ(2, c.Increment());
EXPECT_EQ(3, c.Decrement());
}
}
demo2
- 第1步:编写代码
sample2.h
#ifndef GTEST_SAMPLES_SAMPLE1_H_
#define GTEST_SAMPLES_SAMPLE1_H_
#include <cstring>
class MyString{
private:
const char * c_string_;
const MyString& operator=(const MyString & rhs);
public:
//------------------------------------------------------
static const char * CloneCString(const char * a_c_string);
//-------------------------------------
// The default c'tor constructs a NULL string.
MyString():c_string_(nullptr){};
// Constructs a MyString by cloning a 0-terminated C string.
explicit MyString(const char* a_c_string) : c_string_(nullptr) {
Set(a_c_string);
}
// Copy c'tor
MyString(const MyString& string) : c_string_(nullptr) {
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_;}
//--------------------------------------
const char *c_string() const {return c_string_;}
size_t Length() const { return c_string_ == nullptr ? 0 : strlen(c_string_); }
void Set(const char *a_c_string);
};
#endif
sample2.cpp
#include "sample2.h"
#include <string.h>
const char* MyString::CloneCString(const char* a_c_string) {
if (a_c_string == nullptr){
return nullptr;
}
const size_t len = strlen(a_c_string);
char* const clone = new char [len + 1];
memcpy(clone, a_c_string, len + 1);
return clone;
}
void MyString::Set(const char* a_c_string) {
// Makes sure this works when c_string == c_string_
const char* const temp = MyString::CloneCString(a_c_string);
delete[] c_string_;
c_string_ = temp;
}
- 编写测试用例
#include "sample2.h"
#include "gtest/gtest.h"
namespace {
TEST(MyString, DefaultConstructor) {
const MyString s;
EXPECT_STREQ(nullptr, s.c_string()); // 两个C风格的字符串相等才正确返回
EXPECT_EQ(0u, s.Length());
}
const char kHelloString[] = "Hello, world!";
TEST(MyString, ConstructorFromCString) {
const MyString s(kHelloString);
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
EXPECT_EQ(sizeof(kHelloString)/sizeof(kHelloString[0]) - 1,s.Length());
}
TEST(MyString, CopyConstructor) {
const MyString s1(kHelloString);
const MyString s2 = s1;
EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString));
}
TEST(MyString, Set) {
MyString s;
s.Set(kHelloString);
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
// 当输入指针与MyString对象中已有的指针相同时,Set应该可以工作: 分配一个新的内存来存放它
s.Set(s.c_string());
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
// 可以设置为null
s.Set(nullptr);
EXPECT_STREQ(nullptr, s.c_string()); //两个C风格的字符串相等才正确返回
}
}
第3个例子: Fixtures(预处理)
Fixtures 是测试中非常重要的一部分。他们的主要目的是建立一个固定/已知的环境状态以确保 测试可重复并且按照预期方式运行
我们在单元测试之前会做一些变量初始化等工作,而同一个testcase的不同test之间往往会有一些初始化工作是相同的
我们不想做多余的重复的工作,当然同时也不想设置全局变量。
这个时候我们可以使用Test Fixture。
所谓Test Fixture就是一个类,其包含了公共的设置代码和数据。它必须从googletest的testing命名空间中的Test类派生而来
入门
#include <iostream>
#include <vector>
#include "gtest/gtest.h"
class ListTest : public testing::Test {
protected:
virtual void SetUp() {
_m_list[0] = 11;
_m_list[1] = 12;
_m_list[2] = 13;
}
int _m_list[3];
};
TEST_F(ListTest, FirstElement) {
EXPECT_EQ(11, _m_list[0]);
}
TEST_F(ListTest, SecondElement) {
EXPECT_EQ(12, _m_list[1]);
}
TEST_F(ListTest, ThirdElement) {
EXPECT_EQ(13, _m_list[2]);
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv); // //将命令行参数传递给gtest
return RUN_ALL_TESTS(); // //RUN_ALL_TESTS()运行所有测试案例
}
普通用法
- 编写代码
sample3-inl.h
#pragma once
#include <stddef.h>
// 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_(nullptr) {}
// 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_(nullptr), last_(nullptr), 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 == nullptr) break;
next = node->next();
}
// 2. Resets the member variables.
head_ = last_ = nullptr;
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 nullptr;
}
const QueueNode<E>* const old_head = head_;
head_ = head_->next_;
size_--;
if (size_ == 0) {
last_ = nullptr;
}
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 != nullptr;
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&);
};
编写测试用例
sample_unittest.cpp
#include "sample3-inl.h"
#include "gtest/gtest.h"
namespace {
// To use a test fixture, derive a class from testing::Test.
class QueueTestSmpl3: public testing::Test{
protected: //你应该把成员设为受保护的s.t.它们可以从子类访问。
// 将在运行每个测试之前调用virtual void SetUp()。如果需要初始化变量。否则,可以跳过。
void SetUp() override {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// 每次运行virtual TearDown测试后都将调用virtual TearDown()。
//如果有清理工作要做,就应该定义它。否则,你不必提供它。
virtual void TearDown() {
}
//一些测试使用的辅助函数
static int Double(int n) {
return 2*n;
}
//用于测试Queue::Map()的辅助函数
void MapTester(const Queue<int> *q){
// 创建一个新队列,其中每个元素的大小是q中相应元素的两倍。
const Queue<int> *const new_q = q->Map(Double);
ASSERT_EQ(q->Size(), new_q->Size());
for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
EXPECT_EQ(2 * n1->element(), n2->element());
}
delete new_q;
}
//声明测试要使用的变量。
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
// When you have a test fixture, you define a test using TEST_F
// instead of TEST.
TEST_F(QueueTestSmpl3, DefaultConstructor) {
EXPECT_EQ(0u, q0_.Size());
}
TEST_F(QueueTestSmpl3, Dequeue) {
int * n = q0_.Dequeue();
EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue();
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size());
delete n;
n = q2_.Dequeue();
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size());
delete n;
}
TEST_F(QueueTestSmpl3, EFF) {
EXPECT_EQ(1u, q1_.Size());
}
TEST_F(QueueTestSmpl3, Map) {
MapTester(&q0_);
MapTester(&q1_);
MapTester(&q2_);
}
}
测试函数是否超时
#include <limits.h>
#include <time.h>
#include "gtest/gtest.h"
#include "sample1.h"
#include "sample3-inl.h"
namespace {
// 在这个示例中,我们希望确保每个测试都在~5秒。如果一个测试运行的时间较长,我们认为它是失败。
class QuickTest : public testing::Test {
protected:
void SetUp() override { start_time_ = time(nullptr); }
void TearDown() override {
const time_t end_time = time(nullptr);
//期待测试用例不超过5s
EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
}
time_t start_time_;
};
// 我们从QuickTest继承了一个IntegerFunctionTest。所有使用这个fixture的test都要求必须块
class IntegerFunctionTest : public QuickTest {
// 这里什么都不需要干
};
// Now we can write tests in the IntegerFunctionTest test case.
TEST_F(IntegerFunctionTest, Factorial) {
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
EXPECT_EQ(1, Factorial(0));
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// Tests IsPrime()
TEST_F(IntegerFunctionTest, IsPrime) {
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
class QueueTest : public QuickTest {
protected:
void SetUp() override {
// First, we need to set up the super fixture (QuickTest).
QuickTest::SetUp();
// Second, some additional setup for this fixture.
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
// Now, let's write tests using the QueueTest fixture.
TEST_F(QueueTest, DefaultConstructor) {
EXPECT_EQ(0u, q0_.Size());
}
// Tests Dequeue().
TEST_F(QueueTest, Dequeue) {
int* n = q0_.Dequeue();
EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue();
EXPECT_TRUE(n != nullptr);
EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size());
delete n;
n = q2_.Dequeue();
EXPECT_TRUE(n != nullptr);
EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size());
delete n;
}
}
第4个例子: 参数化测试
在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的,比如上面我们测试素数的代码:我需要传入一系列数值让函数IsPrime去判断是否为True
,在这个测试案例中,我至少复制粘贴了4次,假如参数有50个,100个,怎么办? 这里我们就要用到参数化测试
普通函数参数化测试
#include "sample1.h"
#include "gtest/gtest.h"
namespace {
//1. 告诉gtest你的参数类型是什么
// 你必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型,比如上面的例子,我需要参数化一个int型的参数
class IsPrimeParamTest : public::testing::TestWithParam<int>{
};
// 2. 告诉gtest你拿到参数的值后,具体做些什么样的测试
// 这里,我们要使用一个新的宏TEST_P(parameterized)
TEST_P(IsPrimeParamTest, HandleTrueReturn){
int n = GetParam(); //在TEST_P宏里,使用GetParam()获取当前的参数的具体值。
EXPECT_TRUE(IsPrime(n));
}
// 3.使用INSTANTIATE_TEST_CASE_P这宏来告诉gtest你要测试的参数范围:
// 第一个参数是测试案例的前缀,可以任意取。
// 第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同
// 第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数
INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
}
从上面的框框中的案例名称大概能够看出案例的命名规则: prefix/test_case_name.test.name/index
Google提供了一系列的参数生成的函数:
模板类参数化测试
//
// Created by oceanstar on 2020/11/30.
//
#ifndef TESTADD2_LINE_H
#define TESTADD2_LINE_H
#include <cstddef>
template <std::size_t N>
class Line {
public:
static constexpr std::size_t capacity() {
return N;
}
explicit Line(std::size_t n)
: _length{n < N ? n : N}{};
std::size_t length() const {
return _length;
}
private:
std::size_t _length = 0;
};
#endif //TESTADD2_LINE_H
#include <gtest/gtest.h>
#include <type_traits>
#include "src/line.h"
template <typename T>
class line_tester : public ::testing::Test{};
using test_types = ::testing::Types<
std::integral_constant<std::size_t,2>,
std::integral_constant<std::size_t,3>,
std::integral_constant<std::size_t,5>>;
TYPED_TEST_CASE(line_tester, test_types);
TYPED_TEST(line_tester, get_capacity) {
static constexpr std::size_t n = TypeParam::value;
ASSERT_EQ(n,Line<n>::capacity());
}
TYPED_TEST(line_tester, set_length_preserved) {
Line<TypeParam::value> line{1};
ASSERT_EQ(line.length(),1);
}
TYPED_TEST(line_tester, set_length_trunctated) {
static constexpr std::size_t n = TypeParam::value;
Line<n> line{999};
ASSERT_EQ(line.length(),Line<n>::capacity());
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
demox
- 编写代码
prime_tables.h
// 这提供了确定数字是否为并确定下一个素数。使用此接口在Google测试示例中演示参数化测试的使用。
#pragma once
#include <algorithm>
// 定义接口
class PrimeTable{
public:
virtual ~PrimeTable();
virtual bool IsPrime(int n) const = 0;
virtual int GetNextPrime(int p) const = 0;
};
// Implementation #1
class OnTheFlyPrimeTable : public PrimeTable {
public:
bool IsPrime(int n) const override {
if (n <= 1) return false;
for (int i = 2; i*i <= n; i++) {
if ((n % i) == 0) return false;
}
return true;
}
int GetNextPrime(int p) const override {
if (p < 0) return -1;
for (int n = p + 1;; n++) {
if (IsPrime(n)) return n;
}
}
};
// Implementation #2
class PreCalculatedPrimeTable : public PrimeTable {
public:
explicit PreCalculatedPrimeTable(int max)
: is_prime_size_(max + 1), is_prime_(new bool[max + 1]) {
CalculatePrimesUpTo(max);
}
~PreCalculatedPrimeTable() override { delete[] is_prime_; }
bool IsPrime(int n) const override {
return 0 <= n && n < is_prime_size_ && is_prime_[n];
}
int GetNextPrime(int p) const override {
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;
// 检查每个候选素数(我们知道2是唯一的偶数素数)
for (int i = 2; i*i <= max; i += i%2+1) {
if (!is_prime_[i]) continue;
// 将i的所有倍数(除了i本身)标记为非素数。我们从第i个乘法器开始,因为所有较小的复数都已经被标记了。
for (int j = i*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);
};
- 编写测试用例
************************