问题描述
我以某种方式从 donsoft.io 的示例扩展了 gmock 测试用例,并按如下方式制作:
coinflipper/├── 建设├── 工作区├── coinflipper.cc├── coinflipper.h├── rng.cc└── rng.h
好吧,我将 Rng
类作为 CoinFlipper
构造函数的参数,而是在 CoinFlipper::flipCoin()
方法.
我想知道在这种情况下如何从 Rng
模拟 generate()
?
coinflipper.cc
#include "coinflipper.h";#include CoinFlipper::CoinFlipper() {}CoinFlipper::Result CoinFlipper::flipCoin() const {Rng d_rng;const double val = d_rng.generate(0.0, 1.0);返回 (val
coinflipper.h
#include "rng.h";类 CoinFlipper {民众:枚举结果 { HEADS = 0, TAILS = 1 };显式 CoinFlipper();结果 flipCoin() const;};
rng.h
#ifndef RNG_H#define RNG_H类 Rng {民众:Rng() {};~Rng() {};双生成(双最小,双最大);};#万一
rng.cc
#include "rng.h";double Rng::generate(double min, double max) {返回 0.75;//只是为了测试}
构建
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")cc_图书馆(名称 = "rng",srcs = [rng.cc"],hdrs = [rng.h"],)cc_binary(name = "coinflipper",srcs = [coinflipper.cc",coinflipper.h"],深度 = [":rng",],)cc_test(name = "coinflipper_test",大小=小",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";类 MockRng {民众:MOCK_METHOD2(生成,双(双,双));};测试(CoinFlipper,ShouldReturnHeadsIfRandValueIsLessThanProbability){MockRng;EXPECT_CALL(rng, generate(::testing::DoubleEq(0.0),::testing::DoubleEq(1.0))).Times(::testing::Exactly(1)).WillOnce(::testing::Return(0.25));CoinFlipper coinFlipper;自动结果 = coinFlipper.flipCoin(rng);EXPECT_EQ(CoinFlipper::HEADS,结果);}
巴泽尔输出:
$ bazel test --test_output=all//:coinflipper_test调试:规则rules_cc"表明可以通过修改参数 sha256 =56ac9633c13d74cb71e0546f103ce1c58810e4a76aa8325da593ca4277908d7"来获得规范的可复制形式调试:存储库 rules_cc 在以下位置实例化:/Users/pvd/Downloads/toys/cpp/bazelgtest/coinflipper/WORKSPACE:9:13: 在 <toplevel>存储库规则 http_archive 定义在:/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31:在<toplevel>调试:规则com_google_googletest"表明可以通过修改参数 sha256 = 5cf189eb6847b4f8fc603a3ffff3b0771c08eec7dd4bd961bfd45477dd13eb73"来获得规范的可重现形式调试:存储库 com_google_googletest 在以下位置实例化:/Users/pvd/Downloads/toys/cpp/bazelgtest/coinflipper/WORKSPACE:3:13: 在 <toplevel>存储库规则 http_archive 定义在:/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/external/bazel_tools/tools/build_defs/repo/http.bzl:336:31:在<toplevel>信息:分析目标//:coinflipper_test(加载了 0 个包,配置了 0 个目标).信息:找到 1 个测试目标...失败://:coinflipper_test(见/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/execroot/__main__/bazel-out/darwin-fastbuild/testlogs/coinflipper_test/test.log)信息:来自测试//:coinflipper_test:====================//:coinflipper_test 的测试输出:dyld:懒惰的符号绑定失败:找不到符号:__ZN3Rng8generateEdd参考自:/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/sandbox/darwin-sandbox/72/execroot/__main__/bazel-out/darwin-fastbuild/bin/coinflipper/__lip__main_runfiles预期在:平面命名空间dyld:找不到符号:__ZN3Rng8generateEdd参考自:/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/sandbox/darwin-sandbox/72/execroot/__main__/bazel-out/darwin-fastbuild/bin/coinflipper/__lip__main_runfiles预期在:平面命名空间================================================================================目标//:coinflipper_test 最新:bazel-bin/coinflipper_test信息:经过时间:0.429s,关键路径:0.14s信息:2 个进程:2 个达尔文沙箱.信息:构建完成,1 次测试失败,总共 2 次操作//:coinflipper_test 在 0.1 秒内失败/private/var/tmp/_bazel_pvd/3077b447e558b1418694504407cbcb45/execroot/__main__/bazel-out/darwin-fastbuild/testlogs/coinflipper_test/test.log信息:构建完成,1 次测试失败,总共 2 次操作
定义依赖项(这里的随机生成器)因为局部变量不推荐,做依赖项注入要困难得多(否则不可能),所以我将函数 Rng_t
更改为模板函数并将 Rng 作为参数传递.
在实践中构造随机代可能是繁重的工作,需要初始化它的内部状态,每次调用flipCoin
函数都构造它是浪费.
非虚函数可以mock,一种最常用的策略是使用模板,这里我们把类CoinFlipper的成员函数作为模板函数,然后我们可以用我们的MockRng
测试依赖代码>.
注意,对于模板函数,我们需要在头文件中定义成员函数.
coinflipper.h:
#pragma once#include "rng.h";类 CoinFlipper {民众:枚举结果 { HEADS = 0, TAILS = 1 };模板 结果翻转硬币(Rng_t& rng){const double val = rng.generate(0.0, 1.0);返回 (val
测试文件部分,MockRng
现在没有继承任何东西.我们在这里使用的测试成员函数的类型为 CoinFlipper::flipCoin
.对于生产代码:我们使用类型 CoinFlipper::flipCoin
//#include "mockrng.h";#include "coinflipper.h";#include "gmock/gmock.h";#include "gtest/gtest.h";类 MockRng {民众:MOCK_METHOD2(生成,双(双,双));};测试(CoinFlipper,ShouldReturnHeadsIfRandValueIsLessThanProbability){MockRng;EXPECT_CALL(rng, generate(::testing::DoubleEq(0.0),::testing::DoubleEq(1.0))).Times(::testing::Exactly(1)).WillOnce(::testing::Return(0.25));CoinFlipper coinFlipper;自动结果 = coinFlipper.flipCoin(rng);EXPECT_EQ(CoinFlipper::HEADS,结果);}
在此处查看相关问题:
官方文档:
I somehow extended the gmock test case from donsoft.io's example, and made it as follows:
coinflipper/
├── BUILD
├── WORKSPACE
├── coinflipper.cc
├── coinflipper.h
├── rng.cc
└── rng.h
Well, instead put the Rng
class as a parameter of the constructor of the CoinFlipper
, I made it initialized inside the CoinFlipper::flipCoin()
method.
I was wondering how to mock the generate()
from Rng
in this case?
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;
};
rng.h
#ifndef RNG_H
#define RNG_H
class Rng {
public:
Rng() {};
~Rng() {};
double generate(double min, double max);
};
#endif
rng.cc
#include "rng.h"
double Rng::generate(double min, double max) {
return 0.75; //Just for test
}
BUILD
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 output:
$ 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
Define dependencies(The random generator here) as local variables are not recommended, it's much harder to do dependencies injection(Or it won't be possible), so I change the functions Rng_t
into template function and pass the Rng as a parameter.
In practice to construct a random generation may be heavy work, it needs to initialize its internal status, to construct it every time we call the function flipCoin
is waste.
The non-virtual function can be mocked, one most commonly used strategy is to use the template, here we make the class CoinFlipper's member function as a template function, then we can test the dependency with our MockRng
.
Be aware that for the template function, we need to define the member function in the header file.
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;
}
};
The test file part, MockRng
doesn't inherit anything now. And the test member function we use here has the type CoinFlipper::flipCoin<MockRng>
. For production code: we use the type 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);
}
See related question here:
Mock non-virtual method C++ (gmock)
The official document:
这篇关于如何使用 gmock 在具体类中模拟非虚拟方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!