单元测试:从0到1学习gtest(CLion)

1059 篇文章 274 订阅
  • 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种:

  1. 全局的,所有案例执行前后。
  2. TestSuite级别的,在某一批案例中第一个案例前,最后一个案例执行后。
  3. TestCase级别的,每个TestCase前后。

全局事件

要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。

  1. SetUp()方法在所有案例执行前执行

  2. TearDown()方法在所有案例执行后执行

还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。

实践

入门

环境linux

  1. 下载gtest源码
    加入工程中:
    在这里插入图片描述

在这里插入图片描述
2. 编写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)
  1. 编写代码
    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());
}

全局事件

  1. 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. 第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;
}

  1. 第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


  1. 第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. 第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_);
}

  1. 编写测试用例
#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. 第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;
}



  1. 编写测试用例
#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()运行所有测试案例
}

普通用法

  1. 编写代码

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&);
};



  1. 编写测试用例
    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

  1. 编写代码

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);
};
  1. 编写测试用例
************************

参考

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Cliongtest是两个不同的软件工具。 Clion是一个跨平台的集成开发环境(IDE),主要用于C和C++的开发。它提供了许多功能,包括代码编辑、调试、代码自动补全和代码导航等,使开发者能够更高效地编写和调试代码。 gtest是Google开发的一个C++测试框架,用于编写单元测试和集成测试。它提供了一套丰富的断言和测试宏,可以帮助开发者编写测试用例,并验证代码的正确性。 在使用Clion进行C++开发时,可以集成gtest框架来编写和运行测试。在Clion中,您可以通过添加gtest库和相关的头文件来引用gtest框架,并编写测试用例来验证您的代码逻辑。 关于具体的使用方法和细节,可以参考GCC的官方文档和Clion的官方文档,它们提供了更详细的语法和使用说明。此外,Linux内核的源代码中也有许多使用了GCC Inline和gtest的实际示例,可以帮助您更好地理解和应用它们。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [gcc 汇编guide](https://blog.csdn.net/feelinghappy/article/details/86681501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值