如何使用 gmock 在具体类中模拟非虚拟方法?

pvd

我以某种方式从donsoft.io 的示例中扩展了 gmock 测试用例,并使其如下:

coinflipper/
├── BUILD
├── WORKSPACE
├── coinflipper.cc
├── coinflipper.h
├── rng.cc
└── rng.h

好吧,相反将Rng类作为 的构造函数的参数CoinFlipper,我在CoinFlipper::flipCoin()方法内部对其进行了初始化

我想知道在这种情况下如何模拟generate()from Rng

coinflipper.cc

#include "coinflipper.h"
#include <iostream>

CoinFlipper::CoinFlipper() {}

CoinFlipper::Result CoinFlipper::flipCoin() const {
  Rng d_rng;
  const double val = d_rng.generate(0.0, 1.0);
  return (val < 0.5) ? HEADS : TAILS;
}

int main(int argc, char** argv) {
  CoinFlipper cf;
  CoinFlipper::Result res = cf.flipCoin();
  if (res == 0) {
      std::cout << "head" << std::endl;
  } else {
      std::cout << "tail" << std::endl;
  }
  return 0;
}

coinflipper.h

#include "rng.h"

class CoinFlipper {
 public:
  enum Result { HEADS = 0, TAILS = 1 };

  explicit CoinFlipper();
  Result flipCoin() const;
};

文件

#ifndef RNG_H
#define RNG_H
class Rng {
 public:
  Rng() {};
  ~Rng() {};
  double generate(double min, double max);
};
#endif

手抄报

#include "rng.h"

double Rng::generate(double min, double max) {    
    return 0.75; //Just for test
}

建造

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")

cc_library(
    name = "rng",
    srcs = ["rng.cc"],
    hdrs = ["rng.h"],
)

cc_binary(
    name = "coinflipper",
    srcs = ["coinflipper.cc", "coinflipper.h"],    
    deps = [
        ":rng",
    ],
)

cc_test(
  name = "coinflipper_test",
  size = "small",
  srcs = ["coinflipper_test.cc", "rng.h","coinflipper.h","coinflipper.cc"],
  deps = ["@com_google_googletest//:gtest_main"],
)

coinflipper_test.cc

#include "coinflipper.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

class MockRng {
 public:  
  MOCK_METHOD2(generate, double(double, double));
};

TEST(CoinFlipper, ShouldReturnHeadsIfRandValueIsLessThanProbability) {
  MockRng rng;
  EXPECT_CALL(rng, generate(::testing::DoubleEq(0.0), ::testing::DoubleEq(1.0)))
      .Times(::testing::Exactly(1))
      .WillOnce(::testing::Return(0.25));

  CoinFlipper coinFlipper;
  auto result = coinFlipper.flipCoin<MockRng>(rng);
  EXPECT_EQ(CoinFlipper::HEADS, result);
}

巴泽尔输出:

$ bazel test --test_output=all //:coinflipper_test
DEBUG: Rule 'rules_cc' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = "56ac9633c13d74cb71e0546f103ce1c58810e4a76aa8325da593ca4277908d72"
DEBUG: Repository rules_cc instantiated at:
  /Users/pvd/Downloads/toys/cpp/bazelgtest/coinflipper/WORKSPACE:9:13: in <toplevel>
Repository rule http_archive defined at:
  /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31: in <toplevel>
DEBUG: Rule 'com_google_googletest' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = "5cf189eb6847b4f8fc603a3ffff3b0771c08eec7dd4bd961bfd45477dd13eb73"
DEBUG: Repository com_google_googletest instantiated at:
  /Users/pvd/Downloads/toys/cpp/bazelgtest/coinflipper/WORKSPACE:3:13: in <toplevel>
Repository rule http_archive defined at:
  /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31: in <toplevel>
INFO: Analyzed target //:coinflipper_test (0 packages loaded, 0 targets configured).
INFO: Found 1 test target...
FAIL: //:coinflipper_test (see /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/execroot/__main__/bazel-out/darwin-fastbuild/testlogs/coinflipper_test/test.log)
INFO: From Testing //:coinflipper_test:
==================== Test output for //:coinflipper_test:
dyld: lazy symbol binding failed: Symbol not found: __ZN3Rng8generateEdd
  Referenced from: /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/sandbox/darwin-sandbox/72/execroot/__main__/bazel-out/darwin-fastbuild/bin/coinflipper_test.runfiles/__main__/coinflipper_test
  Expected in: flat namespace

dyld: Symbol not found: __ZN3Rng8generateEdd
  Referenced from: /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/sandbox/darwin-sandbox/72/execroot/__main__/bazel-out/darwin-fastbuild/bin/coinflipper_test.runfiles/__main__/coinflipper_test
  Expected in: flat namespace

================================================================================
Target //:coinflipper_test up-to-date:
  bazel-bin/coinflipper_test
INFO: Elapsed time: 0.429s, Critical Path: 0.14s
INFO: 2 processes: 2 darwin-sandbox.
INFO: Build completed, 1 test FAILED, 2 total actions
//:coinflipper_test                                                      FAILED in 0.1s
  /private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/execroot/__main__/bazel-out/darwin-fastbuild/testlogs/coinflipper_test/test.log

INFO: Build completed, 1 test FAILED, 2 total actions

史前企鹅

不推荐将依赖项(这里的随机生成器)定义为局部变量,做依赖项注入要困难得多(否则不可能),所以我将函数更改Rng_t为模板函数并将Rng作为参数传递。

在实践中构造一个随机代可能是繁重的工作,它需要初始化它的内部状态,每次我们调用函数flipCoin构造它是浪费。

非虚函数可以mock,一种最常用的策略是使用模板,这里我们把类CoinFlipper的成员函数作为模板函数,然后我们可以用我们的MockRng.

请注意,对于模板函数,我们需要在头文件中定义成员函数。

coinflipper.h:

#pragma once
#include "rng.h"

class CoinFlipper {
 public:
  enum Result { HEADS = 0, TAILS = 1 };

  template <typename Rng_t>
  Result flipCoin(Rng_t& rng) {
    const double val = rng.generate(0.0, 1.0);
    return (val < 0.5) ? HEADS : TAILS;
  }
};

测试文件部分,MockRng现在不继承任何东西。我们在这里使用的 test 成员函数的类型为CoinFlipper::flipCoin<MockRng>对于生产代码:我们使用类型CoinFlipper::flipCoin<Rng>

//#include "mockrng.h"

#include "coinflipper.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

class MockRng {
 public:
  MOCK_METHOD2(generate, double(double, double));
};

TEST(CoinFlipper, ShouldReturnHeadsIfRandValueIsLessThanProbability) {
  MockRng rng;
  EXPECT_CALL(rng, generate(::testing::DoubleEq(0.0), ::testing::DoubleEq(1.0)))
      .Times(::testing::Exactly(1))
      .WillOnce(::testing::Return(0.25));

  CoinFlipper coinFlipper;
  auto result = coinFlipper.flipCoin<MockRng>(rng);
  EXPECT_EQ(CoinFlipper::HEADS, result);
}

请参阅此处的相关问题:

模拟非虚拟方法 C++ (gmock)

官方文档:

https://chromium.googlesource.com/external/github.com/google/googletest/+/refs/tags/release-1.8.0/googlemock/docs/CookBook.md

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在C ++中使用GMock模拟非虚拟方法以返回特定值?

如何使用 gmock 来模拟 std::function?

如何使用 gmock 实现阻塞模拟功能?

使用GMock的命名空间模拟方法

使用gmock返回模拟方法参数

如何使用Google Mock使用虚拟和非虚拟方法模拟类?

Gmock 如何模拟不带参数的单行函数?

'(也许你打算使用'->'?)'在gmock测试中

如何使用gmock测试类是否调用了基类的方法

我修改了 gtest/gmock 所以它真的很容易模拟非虚拟函数

gmock:如何将模拟类的指针传递给另一个类?

使用 gmock c++ 在真实对象上调用方法

如何使用Gmock模拟没有运算符==的Function参数

GMock:如何将模拟类变量作为返回值返回

如何使用gmock匹配C ++元组中的一个元素?

如何在Xcode中使用gmock?

如何使用gMock创建Mock对象?

抑制模拟类向量的 gmock 警告

使用Gmock调用成员函数

你如何模拟作为测试 gmock 的那个类的一部分的函数?

使用gmock Matchers将std :: function设置为EXPECT_CALL中的方法参数

为什么不使用volatile参数模拟函数不能为GMock 1.8.0编译

如何使用Gmock在每n次调用中每n次返回一个特定值

如何模拟具体的类无效方法

GMOCK-返回类型为空时如何修改方法参数

如何在gmock Expect_call中对结构参数进行部分匹配

Gmock - 檢查方法是否沒有使用特定參數調用

GMock死亡案例-未调用模拟函数

GMock 调用错误的模拟函数