Dependency injection 对gmock对象应用ON_调用时没有影响
为了好玩,我正在写一个俄罗斯方块游戏。我创建了两个类:“GameBackground”和“Tetromino”。由于“Tetromino”类依赖于“GameBackground”类,因此我使用接口将此依赖注入构造函数中,这样我就可以通过模拟来测试独立于GameBackground类的“Tetromino”类。当我使用gmock创建“GameBackgroundMock”时,测试失败。当我用我自己的模仿Dependency injection 对gmock对象应用ON_调用时没有影响,dependency-injection,googlemock,Dependency Injection,Googlemock,为了好玩,我正在写一个俄罗斯方块游戏。我创建了两个类:“GameBackground”和“Tetromino”。由于“Tetromino”类依赖于“GameBackground”类,因此我使用接口将此依赖注入构造函数中,这样我就可以通过模拟来测试独立于GameBackground类的“Tetromino”类。当我使用gmock创建“GameBackgroundMock”时,测试失败。当我用我自己的模仿 class MyOwnMock : public IGameBackground {
class MyOwnMock : public IGameBackground {
public:
MyOwnMock() : IGameBackground() {
};
bool RequestSpaceOnGrid(TetrominoPositionType requested_coordinates) override{
return true;
}
};
然后测试通过了。显然,我没有正确使用gmock。如果有任何建议可以使用gmock进行绿色测试,我将不胜感激
有关实现的详细信息,请参见以下代码基础
我的初始文件夹结构如下所示: [项目结构][1] [1]: https://i.stack.imgur.com/7h8zT.png 项目顶层的相应文件:
- CMakeLists.txt.in:
- CMakeLists.txt:
- CMakeLists.txt:
- IGameBackground.h
- TetrominoTest.cpp
您正在使用
ON_CALL
来设置调用模拟方法时要返回的默认值。如果您不关心给定的方法在测试中将被调用多少次,并且只设置一个默认值以返回任意次数(但是,expect调用首先被匹配,这是一个较长的过程),那么这非常有用
在您的案例中,最好使用EXPECT\u CALL
明确说明您预期的操作并验证它们:
EXPECT_CALL(a_mock, RequestSpaceOnGrid(current_position)).WillOnce(::testing::Return(true));
如果您将使用此选项,gmock将让您知道测试的错误:调用了
RequestSpaceOnGrid
,但不是使用当前位置调用,而是使用预期位置调用。不确定这是否是预期的,但这是测试中发生的情况。通过调用
ON_调用(一个_mock,RequestSpaceOnGrid(预期的_位置)).WillByDefault(::testing::Return(true))代码>
或者按照夸拉的建议
EXPECT_调用(一个_mock,RequestSpaceOnGrid(expected_position)).WillOnce(::testing::Return(true))代码>
关键的一点是给模拟的RequestSpaceOnGrid方法提供预期位置,而不是当前位置
另一种可能是让测试变为绿色,用::testing::
ON_调用(a_mock,RequestSpaceOnGrid(::testing::)).WillByDefault(::testing::Return(true))代码>
在这种情况下,mock方法将始终返回true,与给定参数无关。Hi,欢迎使用stackoverflow!关于你的问题有一点评论:我想它包含了太多与你所面临的技术问题无关的细节和代码,请阅读以了解如何改进它的细节。我知道,如果你不确定什么工作不好,可能很难判断什么是相关的,但经验法则是,你应该尽量减少你发布的代码量,使其尽可能少,并且代码中仍然显示问题。嗨,夸拉,谢谢你的回答,正如你在上一段中已经注意到的,错误是使用预期位置调用模拟方法,而不是使用当前位置调用模拟方法。
cmake_minimum_required(VERSION 3.11.3)
project(Test)
set(CMAKE_CXX_STANDARD 17)
# Download and unpack googletest at configure time
# (see https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project)
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Add googletest directly to our build. This defines the gtest and gtest_main targets.
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
add_subdirectory(src)
add_subdirectory(test)
cmake_minimum_required(VERSION 3.11.3)
add_library(GameBackground_Lib STATIC GameBackground.cpp)
add_library(Tetromino_Lib STATIC Tetromino.cpp)
#ifndef TEST_IGAMEBACKGROUND_H
#define TEST_IGAMEBACKGROUND_H
#include <utility>
#include <vector>
#include <iostream>
using TetrominoPositionType = std::vector<std::pair<int, int>>;
class IGameBackground {
public:
virtual ~IGameBackground() = default;
virtual bool RequestSpaceOnGrid(TetrominoPositionType) = 0;
};
#endif //TEST_IGAMEBACKGROUND_H
#ifndef TEST_GAMEBACKGROUND_H
#define TEST_GAMEBACKGROUND_H
#include "IGameBackground.h"
#include <tuple>
#include <utility>
#include <vector>
class GameBackground : public IGameBackground {
public:
GameBackground(int, int);
bool RequestSpaceOnGrid(TetrominoPositionType) override;
private:
int m_horizontal_grid_size;
int m_vertical_grid_size;
int m_nr_buttomlines_filled{};
std::vector<std::vector<bool>> m_occupancy_grid{};
};
#endif //TEST_GAMEBACKGROUND_H
#include "GameBackground.h"
GameBackground::GameBackground(int vertical_grid_size, int horizontal_grid_size)
: m_horizontal_grid_size{horizontal_grid_size},
m_vertical_grid_size{vertical_grid_size} {
m_occupancy_grid.resize(vertical_grid_size);
for (auto &row : m_occupancy_grid) {
row.resize(horizontal_grid_size);
}
}
bool GameBackground::RequestSpaceOnGrid(
TetrominoPositionType requested_coordinates) {
bool is_every_coordinate_within_bounds{true};
for (const auto &position : requested_coordinates) {
int pos_x{position.first}, pos_y{position.second};
if (pos_x < 0 || pos_x >= m_vertical_grid_size || pos_y < 0 ||
pos_y >= m_horizontal_grid_size) {
is_every_coordinate_within_bounds = false;
break;
}
}
bool is_request_successfull{false};
if (is_every_coordinate_within_bounds) {
try {
bool is_occupied{false};
for (const auto &position : requested_coordinates) {
int row{position.first}, column{position.second};
is_occupied |= m_occupancy_grid.at(row).at(column);
}
if (!is_occupied) {
for (const auto &position : requested_coordinates) {
int row{position.first}, column{position.second};
m_occupancy_grid.at(row).at(column) = true;
}
is_request_successfull = true;
}
} catch (std::out_of_range const &e) {
std::cerr << e.what() << '\n';
}
}
return is_request_successfull;
}
#ifndef TEST_TETROMINO_H
#define TEST_TETROMINO_H
#include <memory>
#include <utility>
#include <vector>
#include "IGameBackground.h"
enum class Direction { left, right, down };
using TetrominoPositionType = std::vector<std::pair<int, int>>;
class Tetromino {
public:
Tetromino(IGameBackground&, TetrominoPositionType);
TetrominoPositionType getPosition();
void setPosition(TetrominoPositionType);
void moveOneStep(Direction);
private:
TetrominoPositionType m_position;
IGameBackground& m_game_background;
};
#endif //TEST_TETROMINO_H
#include "Tetromino.h"
#include <utility>
Tetromino::Tetromino(IGameBackground &game_background,
TetrominoPositionType init_position)
: m_game_background{game_background},
m_position{std::move(init_position)} {};
TetrominoPositionType Tetromino::getPosition() { return m_position; }
void Tetromino::setPosition(TetrominoPositionType position) {
m_position = std::move(position);
}
void Tetromino::moveOneStep(Direction direction) {
TetrominoPositionType position = getPosition();
switch (direction) {
case Direction::down:
for (auto &pos : position) {
++pos.first;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
case Direction::left:
for (auto &pos : position) {
--pos.second;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
case Direction::right:
for (auto &pos : position) {
++pos.second;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
}
}
cmake_minimum_required(VERSION 3.11.3)
add_executable(TetrominoTest TetrominoTest.cpp)
target_link_libraries(TetrominoTest gmock_main gtest_main Tetromino_Lib)
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "../src/IGameBackground.h"
#include "../src/Tetromino.h"
//class MyOwnMock : public IGameBackground {
//public:
// MyOwnMock() : IGameBackground() {
// };
// bool RequestSpaceOnGrid(TetrominoPositionType requested_coordinates) override{
// return true;
// }
//};
class GameBackgroundMock : public IGameBackground {
public:
GameBackgroundMock() : IGameBackground() {
};
MOCK_METHOD(bool, RequestSpaceOnGrid,
(TetrominoPositionType requested_coordinates), (override));
};
class MoveTetromino : public ::testing::Test {
protected:
MoveTetromino() : unit_under_test(a_mock, init_position) {};
TetrominoPositionType init_position{{0, 0},
{0, 1},
{0, 2},
{0, 3}};
Tetromino unit_under_test;
GameBackgroundMock a_mock;
//MyOwnMock a_mock;
};
TEST_F(MoveTetromino, move_right) {
TetrominoPositionType current_position{init_position};
TetrominoPositionType expected_position{init_position};
for (auto &elem : expected_position) {
elem.second++;
}
ON_CALL(a_mock, RequestSpaceOnGrid(current_position)).WillByDefault(::testing::Return(true));
unit_under_test.moveOneStep(Direction::right);
TetrominoPositionType actual_position = unit_under_test.getPosition();
EXPECT_EQ(expected_position, actual_position);
}
EXPECT_CALL(a_mock, RequestSpaceOnGrid(current_position)).WillOnce(::testing::Return(true));