GoogleTest之Actions的用法

news2024/11/21 0:27:57

目录

  • 返回值
  • Actions的组合
  • 验证复杂参数
  • mock副作用
  • 改变mock对象的行为
  • 设置返回类型的默认值
  • 使用自定义函数作为Actions

通用示例

namespace mock_action {
class Foo {
public:
    virtual ~Foo() {}
    virtual int& GetBar() = 0;      // 1
    virtual int GetPointerValue() = 0;  // 2
    virtual int CalculateBar() = 0;  // 3
    virtual int Sign(int x) = 0;  // 4
    virtual int DoCombineActions() = 0;  // 5
    virtual void Mutate(bool mutate, int *value) = 0;  // 6
};

class MockFoo : public Foo {
public:
  MOCK_METHOD(int&, GetBar, (), (override));
  MOCK_METHOD(int, GetPointerValue, (), (override));
  MOCK_METHOD(int, CalculateBar, (), (override));
  MOCK_METHOD(int, Sign, (int x), (override));
  MOCK_METHOD(int, DoCombineActions, (), (override));
  MOCK_METHOD(void, Mutate, (bool mutate, int* value), (override));
}; 
}

返回值

  1. mock函数使用ReturnRef()返回引用
// mock类函数为通用示例中的1

TEST(test_action, mock_action_returnref) {
  mock_action::MockFoo foo;
  int x = 10;
  EXPECT_CALL(foo, GetBar()).WillOnce(ReturnRef(x));
  EXPECT_EQ(foo.GetBar(), 10);
}

如果将ReturnRef(x)换成Return(x)或者Return(10)都会报语法错误。
2. 返回live value(可以理解为实时值)
Return(x)保存了x的一份拷贝,在后续测试中会一直使用这个拷贝值而不会随着x的改变而改变,在x有改动的情况下就无法满足要求了。要mock的函数的返回值必须是引用才能使用ReturnRef,且不能使用std::ref()(原因TODO)。此时可以通过ReturnPointee解决该问题。

// mock类函数为通用示例中的 2
TEST(test_action, mock_action_returnpointer) {
    mock_action::MockFoo foo;
    int x = 0;
    EXPECT_CALL(foo, GetPointerValue())
    .WillRepeatedly(ReturnPointee(&x));  // 这样做的意义是什么?
    x = 42;
    EXPECT_EQ(foo.GetPointerValue(), 42);
}

注意,这里action中返回值可以根据动态值而改变。这里mock函数返回值并不是指针,但是action可以使用ReturnPointee,这一点和ReturnRef不同。

Actions的组合

想要执行不止一个action,可以使用::testing::DoAll实现actions的组合:

// mock类函数为通用示例中的 5
TEST(test_action, mock_combine_actions) {
  mock_action::MockFoo foo;
  EXPECT_CALL(foo, DoCombineActions()).WillOnce(DoAll(
    [](){
      std::cout << "DoCombineActions 1" << std::endl;
    },
    [] () {
      std::cout << "DoCombineActions 2" << std::endl;
    },
    Return(10)
  ));
  EXPECT_EQ(foo.DoCombineActions(), 10);
}

输出(EXPECT_CALL改成ON_CALL会报warning?TODO):
在这里插入图片描述

验证复杂参数

TODO

mock副作用

  1. 如果想改变出参,可以使用SetArgPointee()
// mock类函数为通用示例中的 6
TEST(test_action, mock_side_effects01) {
    mock_action::MockFoo mutator;
    EXPECT_CALL(mutator, Mutate(true, _))
    .WillOnce(SetArgPointee<1>(5));
    int x = 1;
    mutator.Mutate(true, &x);
    EXPECT_EQ(x, 5);
}

SetArgPointee<index>(value) index是从0开始的下标;且value必须支持赋值或者拷贝构造。
如果仍然要返回值,使用DoAll执行这两个action,但要注意需要将Return放到后面
如果出参是数组,则需要使用 SetArrayArgument<N>(first, last)(N只能从0开始吗?TODO)

TEST(test_action, mock_size_effects03) {
    mock_action::MockArrayMutator mutator;
    int values[5] = {1, 2, 3, 4, 5};
    int values02[15] = {1,2,3};
    EXPECT_CALL(mutator, Mutate(NotNull(), 15))
    .WillOnce(SetArrayArgument<0>(values, values+5));  // N只能从0开始?
    mutator.Mutate(values02, 15);
    std::for_each(values02, values02+15, [](int n){
      std::cout << n << std::endl;
    });
}

改变mock对象的行为

如果想通过某个调用改变mock对象的行为,可以使用 ::testing::InSequence 指定这个调用之前和之后的不同行为
mock类代码

class ChangeBehavior {
public:
  virtual ~ChangeBehavior() {}
  virtual bool IsDirty() = 0;
  virtual void Flush() = 0;
  virtual bool FlushIfDirty() = 0;
};

class MockChangeBehavior : public ChangeBehavior {
public:
  MOCK_METHOD(bool, IsDirty, (), (override));
  MOCK_METHOD(void, Flush, (), (override));
  MOCK_METHOD(bool, FlushIfDirty, (), (override));
};

测试代码

TEST(test_action, mock_change01) {
  mock_action::MockChangeBehavior my_mock;
  {
      InSequence seq;
      EXPECT_CALL(my_mock, IsDirty()).WillRepeatedly(
        DoAll(
          [] () { std::cout << "IsDirty(), return true" << std::endl; },
          Return(true)
          )
      );
      EXPECT_CALL(my_mock, Flush()).WillOnce(
        [] () { std::cout << "Flush" << std::endl; }
      );
      EXPECT_CALL(my_mock, IsDirty()).WillRepeatedly(
        DoAll(
          [] () { std::cout << "IsDirty(), return false" << std::endl; },
          Return(false)
          )
      );
  }
  
  EXPECT_EQ(my_mock.IsDirty(), true);
  my_mock.Flush();
  EXPECT_EQ(my_mock.IsDirty(), false);
}

说明:这段代码会在my_mock.Flush()调用之前调用my_mock.IsDirty()时返回true,在my_mock.Flush()调用之后调用my_mock.IsDirty()返回false。

  • 保存结果 TODO

设置返回类型的默认值

  1. 通过::testing::DefaultValue指定默认值。
// mock类函数为通用示例中的 3和4
TEST(test_action, mock_default_value) {
    int x = 3;
    DefaultValue<int>::Set(x);
    mock_action::MockFoo foo;
    EXPECT_CALL(foo, CalculateBar());  
    EXPECT_CALL(foo, Sign(_));  // 对Sign也是有效的
    EXPECT_EQ(foo.CalculateBar(), 3);
    EXPECT_EQ(foo.Sign(2), 3);
    DefaultValue<int>::Clear();
}

这样设置后对相同类型是全局有效的;要注意使用SetClear的顺序。

  1. 如果要求相同类型默认返回值不同,可以使用ON_CALL+EXPECT_CALL的方式实现这一目的
// mock类函数为通用示例中的 4
TEST(test_action, mock_default_action) {
    mock_action::MockFoo foo;
    ON_CALL(foo, Sign(_)).WillByDefault(Return(-1));
    ON_CALL(foo, Sign(0)).WillByDefault(Return(0));
    ON_CALL(foo, Sign(Gt(0))).WillByDefault(Return(1));
    EXPECT_CALL(foo, Sign(_)).Times(AnyNumber());
    EXPECT_EQ(foo.Sign(5), 1);
    EXPECT_EQ(foo.Sign(-9), -1);
    EXPECT_EQ(foo.Sign(0), 0);
}

测试程序的输出是没有警告的。
Note that both ON_CALL and EXPECT_CALL have the same “later statements take precedence” rule, but they don’t interact. That is, EXPECT_CALLs have their own precedence order distinct from the ON_CALL precedence order.

使用自定义函数作为Actions

class ACFoo {
public:
  virtual ~ACFoo(){};
  virtual int Sum(int x, int y) = 0;
  virtual bool ComplexJob(int x) = 0;
};

class ACMockFoo : public ACFoo {
public:
  MOCK_METHOD(int, Sum, (int x, int y), (override));
  MOCK_METHOD(bool, ComplexJob, (int x), (override));
};

int CalculateSum(int x, int y) {
  std::cout << "Enter in CalculateSum, x: " << x << ", y: " << y << std::endl;
  return x + y;
}

int Sum3(int x, int y, int z) {
  std::cout << "Sum3" << std::endl;
  return x + y + z;
}

int NewPermanentCallback(const std::function<int(int,int,int)>& f, int x) {
  std::cout << "Enter in NewPermanentCallback" << std::endl;
  return f(1,2,3);
}

class Helper {
public:
  bool ComplexJob(int x) {
    std::cout << "Helper::ComplexJob" << std::endl;
    return true;
  }
};

验证代码:

TEST(test_action, mock_use_functor01) {
  mock_action::ACMockFoo foo;
  mock_action::Helper helper;
  EXPECT_CALL(foo, Sum(_, _))
    .WillOnce(&mock_action::CalculateSum)  // 1
    .WillRepeatedly(Invoke(mock_action::NewPermanentCallback(mock_action::Sum3, 1)));  // 2 这里不正确,编译报错TODO
  EXPECT_CALL(foo, ComplexJob(_))
  .WillOnce(Invoke(&helper, &mock_action::Helper::ComplexJob))  // 3
  .WillOnce([] { std::cout << "lambda 1 " << std::endl; return true;})  // 4
  .WillRepeatedly([](int x) { std::cout << "lambda 2 " << std::endl; return x > 0;});  // 5

  foo.Sum(5,6);
  foo.ComplexJob(10);
  foo.ComplexJob(-1);
  foo.ComplexJob(3);
}

分析:

  1. 使用全局自由函数,注意函数签名要一致
  2. 这里有问题,编译出错,需要解决
  3. 使用其他对象的成员函数,函数签名也要保持一致
  4. 4和5都是用lambda表达式,但是4的签名和mock函数不一样,为什么没有报错?TODO

如果作为action的这些函数参数个数多于mock函数,可以通过预先绑定的方式(类似std::bind)

using ::testing::Invoke;

class MockFoo : public Foo {
 public:
  MOCK_METHOD(char, DoThis, (int n), (override));
};

char SignOfSum(int x, int y) {
  const int sum = x + y;
  return (sum > 0) ? '+' : (sum < 0) ? '-' : '0';
}

TEST_F(FooTest, Test) {
  MockFoo foo;

  EXPECT_CALL(foo, DoThis(2))
      .WillOnce(Invoke(NewPermanentCallback(SignOfSum, 5)));
  EXPECT_EQ('+', foo.DoThis(2));  // Invokes SignOfSum(5, 2).
}
  • 但是这里的NewPermanentCallback是什么?TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/640650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux CentOS7虚拟机配置静态IP并允许上网的配置方法

文章目录 前言一、开启本地电脑VMnet8二、Linux配置静态IP1. NAT模式设置2. 开启虚拟机登录root用户3. 执行命令设置静态IP4. 重启网卡① 重启网卡 (正常)② 重启网卡 (异常)③ 解决方式&#xff1a;禁用NetworkManager 5. 查看ip6. 本地电脑cmd窗口ping虚拟机7. 虚拟机ping本地…

Golang每日一练(leetDay0095) 第一个错误的版本、完全平方数

目录 278. 第一个错误的版本 First Bad Version &#x1f31f; 279. 完全平方数 Perfect Squares &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日…

springboot的数据访问和数据视图

当使用 Spring Boot 进行数据访问时&#xff0c;我们可以选择使用 MyBatis 或 JPA&#xff08;Java Persistence API&#xff09;来实现增删改查操作。下面我将分别给出使用这两种方式整合数据访问的详细步骤和示例&#xff0c;同时结合 Thymeleaf 实现数据展现。 方式一: 使用…

AI实战营:语义分割与MMSegmentation

目录 OpenMMLab图像分割算法库MMSegmentation 深度学习下的语义分割模型 全卷积网络Fully Convolutional Network 201 ​编辑 上下文信息与PSPNet模型 空洞卷积与DeepLab模型 语义分割算法总结 语义分割 前沿算法 SegFormer K-Net MaskFormer Mask2Former Seg…

PySide2 or PyQt5???该如何抉择???

1. 区别 Qt库里面有非常强大的图形界面开发库&#xff0c;但是Qt库是C语言开发的&#xff0c;PySide2、PyQt5可以让我们通过Python语言使用Qt。 但是 PySide2、PyQt5 这两者有什么区别呢&#xff1f; 可以形象地这样说&#xff1a; PySide2 是Qt的 亲儿子 &#xff0c; PyQt5 …

面向对象程序设计|静态友元

题目一&#xff1a;复数运算 题目描述&#xff1a; 复数类的声明如下 要求如下&#xff1a; 1. 实现复数类和友元函数addCom和outCom&#xff1b; 2. 参考addCom函数为复数类增加一个友元函数minusCom&#xff0c;用于实现两个复数的减法&#xff1b; 3. 在main函数中&…

待办事项JS:DHTMLX To Do List 1.2 cRACK

DHTMLX To Do List用于有效任务管理的DHTMLX JavaScript 待办事项列表 使用 JavaScript/HTML 5 中的待办事项列表来管理您的任务并确定其优先级。将组件连接到 DHTMLX 甘特图&#xff0c;并允许用户以简单直观的方式快速组织他们的业务流程。 DHTMLX JavaScript 待办事项列表的…

chatgpt赋能python:Python建模块最佳实践

Python建模块最佳实践 Python是一种灵活、易于使用的编程语言&#xff0c;因其强大的模块化支持和丰富的第三方模块而备受推崇。本文将介绍Python建模块的最佳实践&#xff0c;以便帮助开发人员创建可重用、可维护和易于测试的Python模块。 基本概念 在Python中&#xff0c;…

创业很长时间以后…

创业过很长时间以后…综合能力是有滴 创业和打工后的思维习惯 为了效率&#xff0c;一般情况是这样滴 趣讲大白话&#xff1a;区别还是有滴 【趣讲信息科技195期】 **************************** 创业还是很难滴 每年成立很多新公司 有很多公司关门 公司平均生存时间&#xff1…

右值引用以及move移动语义和forward 完美转发

右值引用 右值引用最简单的作用&#xff1a;可以避免无谓的复制&#xff0c;提高了程序性能&#xff08;在移动构造函数中有体现&#xff09;。 什么是右值 最基本的解释&#xff1a; 左值可以取地址、位于等号左边&#xff1b; 右值没法取地址&#xff0c;位于等号右边。&…

UnityVR--组件9--VideoPlayerAudioSource

目录 前言 视频组件VideoPlayer参数解释 RenderMode渲染方式 VideoPlayer类中的API 音频组件AudioSource参数解释 AudioSource类中的常见API&简单应用 前言 在之前的VR场景中已经使用过VideoPlayer播放视频&#xff08;Unity.UI的交互&#xff08;6&#xff09;-播放…

chatgpt赋能python:Python怎么快速读取一组图片的RGB值?

Python怎么快速读取一组图片的RGB值&#xff1f; 简介 Python是一种非常流行的程序设计语言&#xff0c;它具有易于学习、简洁明了的语法和强大的功能。Python被广泛应用于数据分析、人工智能、科学计算、Web开发、游戏开发等领域。在这篇文章中&#xff0c;我们将介绍如何使…

chatgpt赋能python:Python快速缩进技巧与优化提升

Python快速缩进技巧与优化提升 介绍 在Python中&#xff0c;缩进是代码块的唯一标识符。这种缩进机制使得Python代码看起来更加清晰和易于阅读。同时&#xff0c;正确的缩进也是Python程序能否正常运行的重要因素。然而&#xff0c;大量的缩进可能会导致程序员的效率降低&…

Android系统Handler详解

目录 一&#xff0c;背景介绍 1.1 简介 1.2 核心概念 1.3 Handler 背后的生产者-消费者模型 二&#xff0c;Handler机制原理 2.1 消息模型 2.2 Handler原理概述 2.3 Handler与管道通信 三&#xff0c;实战 3.1 创建 Handler 3.2 子线程向主线程 3.3 主线程向子线程…

C/C++爱心代码“你把握不住的,让哥来~”祝你找到另一半

目录 第一种心形 加点好玩的 最后一忠心形&#xff08;会变色的爱心&#xff09; 618多得图书活动来啦 第一种心形 这次需要用到头文件#include<windows.h> #include<stdio.h> #include<windows.h> 以下是完整代码 #include<stdio.h> #include<…

chatgpt赋能python:Python中如何使用Math库进行数学计算

Python中如何使用Math库进行数学计算 Python是一种功能强大的编程语言&#xff0c;但对于许多数字计算、三角函数和其他复杂的数学问题&#xff0c;Python本身并不提供内置支持。为了解决这些问题&#xff0c;Python提供了一个名为Math的库。本文将介绍如何引入Math库&#xf…

【王道·操作系统】第三章 内存管理【未完】

一、内存管理 1.1 内存的基础知识 内存可存放数据&#xff0c;程序执行前需要先放到内存中才能被CPU处理——缓和CPU与硬盘之间的速度矛盾内存地址从0开始&#xff0c;每个地址对应一个存储单元 按字节编址&#xff1a;每个存储单元大小为1字节(B)&#xff0c;即8个二进制位按…

OJ Prime Gap

目录 1.题目 2.中文翻译 3.题意 4.代码 5.知识点 range的倒序处理&#xff1a; 1.题目 Prime Gap Description The sequence of n − 1 consecutive composite numbers (positive integers that are not prime and not equal to 1) lying between two successive prime…

软考A计划-2023系统架构师-知识点集锦(3/4)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

DAY20:二叉树(十)最大二叉树+合并二叉树

文章目录 654.最大二叉树思路遍历顺序 完整版变量作用域的问题 修改后的完整版递归进一步理解关于终止条件 优化时间复杂度和空间复杂度的优化补充&#xff1a;二叉树的高度logn 617.合并二叉树思路完整版定义新二叉树的写法 654.最大二叉树 本题做的时候也卡了一些问题&#…