微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在gmock对象上应用ON_CALL无效

如何解决在gmock对象上应用ON_CALL无效

出于有趣的目的,我正在编写一个俄罗斯方块游戏。我创建了两个类:“ GameBackground”和“ Tetromino”。由于“ Tetromino”类对“ GameBackground”类具有依赖关系,因此我使用接口将该依赖项注入到构造函数中,这样我就可以通过模拟它来独立于GameBackground类来测试“ Tetromino”类。当我使用gmock创建“ GameBackgroundMock”时,测试失败。当我使用自己的模拟游戏


    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

    # see https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
    cmake_minimum_required(VERSION 2.8.2)
    
    project(googletest-download NONE)
    
    include(ExternalProject)
    ExternalProject_Add(googletest
      GIT_REPOSITORY    https://github.com/google/googletest.git
      GIT_TAG           master
      SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
      BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
      CONfigURE_COMMAND ""
      BUILD_COMMAND     ""
      INSTALL_COMMAND   ""
      TEST_COMMAND      ""
    )

  • CMakeLists.txt

    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)

src文件夹的内容

  • CMakeLists.txt

    cmake_minimum_required(VERSION 3.11.3)
    add_library(GameBackground_Lib STATIC GameBackground.cpp)
    add_library(Tetromino_Lib STATIC Tetromino.cpp)

  • IGameBackground.h

    #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

  • GameBackground.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

  • GameBackground.cpp

    #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;
    }

  • Tetromino.h

    #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

  • Tetromino.cpp

    #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;
        }
    }

测试文件

内容
  • CMakeLists.txt

    cmake_minimum_required(VERSION 3.11.3)
    add_executable(TetrominoTest TetrominoTest.cpp)
    target_link_libraries(TetrominoTest gmock_main gtest_main Tetromino_Lib)

  • TetrominoTest.cpp

    #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},2},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);
    }

解决方法

您正在使用ON_CALL来设置在调用模拟方法时要返回的默认值。如果您不在乎测试中给定方法将被调用多少次,而您只是将默认值设置为可以返回任意次数(这是很长的话),这很有用。

在您的情况下,最好使用EXPECT_CALL来明确说明您预期的动作并验证它们:

EXPECT_CALL(a_mock,RequestSpaceOnGrid(current_position)).WillOnce(::testing::Return(true));

如果您将使用此方法,则gmock会告诉您测试出了什么问题:RequestSpaceOnGrid被调用,但是不是通过current_position而是通过expected_position进行的。不知道是否可以预期,但这就是测试中发生的情况。

,

通过以下任一方式通过测试

ON_CALL(a_mock,RequestSpaceOnGrid(expected_position)).WillByDefault(::testing::Return(true));

或Quarra建议

EXPECT_CALL(a_mock,RequestSpaceOnGrid(expected_position)).WillOnce(::testing::Return(true));

关键点是为模拟的RequestSpaceOnGrid方法提供 expected_position 而不是 current_position

另一种可能性是使测试变为绿色,以使用::testing::_调用模拟方法

ON_CALL(a_mock,RequestSpaceOnGrid(::testing::_)).WillByDefault(::testing::Return(true));

在这种情况下,模拟方法将始终返回true,而与给定参数无关。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。