Gtest/Gmock探究(一)-- 经典示例代码

接下来几篇文档记录,打算探究一下Google C++的单元测试框架Gtest/Gmock.
    · 先给出经典初识Gtest框架的代码(AccountMangerTest)
    · 紧接着探究TEST宏原理
    · 随着是Gmock的MOCK_METHODX系列宏的探究
代码结构:
    这里写图片描述
    首先,准备gmock的源代码(我这里是gmock-1.7.0),并且按照官方文档编译通过。(点击这里下载gmock1.7.0)

补充gmock的编译:
    cd gmock-1.7.0
    ./configure && make

    其次,将我们的代码文件夹命名为AccountManagerTest,与gmock-1.7.0在同一级目录(代码目录结构如上图所示)。下面列出我们的测试代码文件内容,以及编译和运行方法:

// Account.h
#pragma once
#include <string>
using namespace std;

class Account {
private:
    string m_strAccountId;
    long m_nBalance;

public:
    Account() : m_strAccountId(""), m_nBalance(0) {;}

    Account(const string &initAccountId, long initialBalance) : m_strAccountId(initAccountId), m_nBalance(initialBalance) {;}

    bool isNil() {
        return m_strAccountId == "";
    }

    void debit(long amount) {
        m_nBalance -= amount;
    }

    void credit(long amount) {
        m_nBalance += amount;
    }

    long getBalance() const {
        return m_nBalance;
    }

    string getAccountId() const {
        return m_strAccountId;
    }

    void SetId(const string &id) {
        m_strAccountId = id;
    }
}; 
// AccountManager.h
#pragma once
#include "Account.h"

class AccountManager {
public:
    virtual Account findAccountForUser(const string &userId) = 0;
    virtual void updateAccount(const Account &account) = 0;
}; 
// AccountService.h
#pragma once
#include <string> 
using namespace std;

#include "Account.h" 
#include "AccountManager.h" 

class AccountService 
{ 
private: 
   AccountManager* pAccountManager; 

public: 
   AccountService(); 

   void setAccountManager(AccountManager* pManager); 

    bool withdraw(const string &id, long amount); 

    void topup(const string &id, long amount); 

   void transfer(const string& senderId, const string& beneficiaryId, long amount); 
};
// AccountService.cpp
#include "AccountService.h"

AccountService::AccountService() {
    pAccountManager = NULL;
}

void AccountService::setAccountManager(AccountManager *pManager) {
    pAccountManager = pManager;
}

void AccountService::transfer(const string &senderId, const string &beneficiaryId, long amount) {
    Account sender = pAccountManager->findAccountForUser(senderId);
    Account beneficiary = pAccountManager->findAccountForUser(beneficiaryId);

    sender.debit(amount);
    beneficiary.credit(amount);

    pAccountManager->updateAccount(sender);
    pAccountManager->updateAccount(beneficiary);
}

bool AccountService::withdraw(const string &id, long amount) {
    Account owner = pAccountManager->findAccountForUser(id);
    if (owner.isNil()) {
        return false;
    }
    owner.debit(amount); 
    pAccountManager->updateAccount(owner);
    return true;
}

void AccountService::topup(const string &id, long amount) {
    Account owner = pAccountManager->findAccountForUser(id);
    if (owner.isNil()) {
        owner.SetId(id);
    }
    owner.credit(amount); 
    pAccountManager->updateAccount(owner);
}
// AccountHelper.h
#include "Account.h"
#include <map>
using namespace std;

class AccountHelper {
private:
    map<string, Account> m_mapAccounts;
    // an internal map to store all Accounts for test

public:
    AccountHelper(map<std::string, Account> &mAccount);
    AccountHelper() {;}

    void updateAccount(const Account &account);

    Account findAccountForUser(const std::string &userId);
}; 
// AccountHelper.cpp
#include "AccountHelper.h"

AccountHelper::AccountHelper(std::map<std::string, Account> &mAccount) {
    m_mapAccounts = mAccount;
}

void AccountHelper::updateAccount(const Account &account) {
    m_mapAccounts[account.getAccountId()] = account;
}

Account AccountHelper::findAccountForUser(const std::string &userId) {
    if (m_mapAccounts.find(userId) != this->m_mapAccounts.end()) 
        return this->m_mapAccounts[userId];
    return Account();
} 
// MockAccountManager.h
#include "AccountManager.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"

class MockAccountManager : public AccountManager {
public:
    MOCK_METHOD1(findAccountForUser, Account(const string&));

    MOCK_METHOD1(updateAccount, void(const Account&));
}; 
// main.cpp
#include "AccountHelper.h"
#include "MockAccountManager.h"
#include "AccountService.h"

TEST(AccountServiceTest, transferTest) {
    map<std::string, Account> mapAccounts;
    mapAccounts["A"] = Account("A", 3000);
    mapAccounts["B"] = Account("B", 2000);
    AccountHelper helper(mapAccounts);

    MockAccountManager *pManager = new MockAccountManager();

    EXPECT_CALL(*pManager, findAccountForUser(testing::_))
        .Times(2)
        .WillRepeatedly(testing::Invoke(&helper, &AccountHelper::findAccountForUser));

    EXPECT_CALL(*pManager, updateAccount(testing::_)).WillRepeatedly(
        testing::Invoke(&helper, &AccountHelper::updateAccount));

    AccountService as;
    as.setAccountManager(pManager);
    as.transfer("A", "B", 1005);

    EXPECT_EQ(1995, helper.findAccountForUser("A").getBalance());
    EXPECT_EQ(3005, helper.findAccountForUser("B").getBalance());

    delete pManager;
}

TEST(AccountServiceTest, topupTest) {
    AccountHelper helper;

    MockAccountManager *pManager = new MockAccountManager();

    EXPECT_CALL(*pManager, findAccountForUser(testing::_))
        .WillRepeatedly(testing::Invoke(&helper, &AccountHelper::findAccountForUser)); 

    EXPECT_CALL(*pManager, findAccountForUser("B"))
        .WillRepeatedly(testing::Invoke(&helper, &AccountHelper::findAccountForUser));

    EXPECT_CALL(*pManager, findAccountForUser("A"))
        .WillRepeatedly(testing::Return(Account("A", 5))); 

    EXPECT_CALL(*pManager, updateAccount(testing::_)).WillRepeatedly(
        testing::Invoke(&helper, &AccountHelper::updateAccount));

    AccountService as;
    as.setAccountManager(pManager);
    as.topup("B", 1005);
    as.topup("A", 5); 

    EXPECT_EQ(10, helper.findAccountForUser("A").getBalance());
    EXPECT_EQ(1005, helper.findAccountForUser("B").getBalance());

    delete pManager;
}

int main(int argc, char **argv) {
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
# Makefile.linux
# Makefile for Mr.H CSDN blog - Do a research on Gtest/Gmock 2017.12.14
# Make command to use for dependencies
MAKE=make
RM=rm
MKDIR=mkdir

# Only "Debug" version for test
CFG=Debug

#
# Configuration: Debug
#
ifeq "$(CFG)" "Debug"
OUTDIR=Debug
OUTFILE=$(OUTDIR)/test
CFG_INC=-I../gmock-1.7.0/include -I../gmock-1.7.0/gtest/include 
CFG_LIB=-lpthread -lc -lm -lrt -lgtest -lgmock -L ../gmock-1.7.0/lib/.libs \
    -L ../gmock-1.7.0/gtest/lib/.libs 
CFG_OBJ=
COMMON_OBJ=$(OUTDIR)/AccountHelper.o $(OUTDIR)/AccountService.o $(OUTDIR)/main.o 
OBJ=$(COMMON_OBJ) $(CFG_OBJ)
ALL_OBJ=$(OUTDIR)/AccountHelper.o $(OUTDIR)/AccountService.o \
    $(OUTDIR)/main.o -lpthread -lc -lm -lrt -lgtest -lgmock -L ../gmock-1.7.0/lib/.libs \
    -L ../gmock-1.7.0/gtest/lib/.libs 

COMPILE=g++ -c   -g -o "$(OUTDIR)/$(*F).o" $(CFG_INC) $<
LINK=g++  -g -o "$(OUTFILE)" $(ALL_OBJ)

# Pattern rules
$(OUTDIR)/%.o : %.cpp
    $(COMPILE)

# Build rules
all: $(OUTFILE)

$(OUTFILE): $(OUTDIR)  $(OBJ)
    $(LINK)
    -cp Debug/test ./

$(OUTDIR):
    $(MKDIR) -p "$(OUTDIR)"

# Rebuild this project
rebuild: cleanall all

# Clean this project
clean:
    $(RM) -f $(OUTFILE)
    $(RM) -f $(OBJ)

# Clean this project and all dependencies
cleanall: clean
endif

说明:
    这里只给出linux上的编译运行方式(代码使用标准STL库,所以Windows上也能运行,编译方法大家自己研究)
    编译: make -f Makefile.linux
    运行:./test
    输出:

[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from AccountServiceTest
[ RUN      ] AccountServiceTest.transferTest
[       OK ] AccountServiceTest.transferTest (0 ms)
[ RUN      ] AccountServiceTest.topupTest
[       OK ] AccountServiceTest.topupTest (0 ms)
[----------] 2 tests from AccountServiceTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 2 tests.
  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Base path: /home/dama/demo02_ws Source space: /home/dama/demo02_ws/src Build space: /home/dama/demo02_ws/build Devel space: /home/dama/demo02_ws/devel Install space: /home/dama/demo02_ws/install #### #### Running command: "make cmake_check_build_system" in "/home/dama/demo02_ws/build" #### -- Using CATKIN_DEVEL_PREFIX: /home/dama/demo02_ws/devel -- Using CMAKE_PREFIX_PATH: /home/dama/demo02_ws/devel;/home/dama/demo01_ws/devel;/opt/ros/noetic -- This workspace overlays: /home/dama/demo02_ws/devel;/home/dama/demo01_ws/devel;/opt/ros/noetic -- Found PythonInterp: /usr/bin/python3 (found suitable version "3.8.10", minimum required is "3") -- Using PYTHON_EXECUTABLE: /usr/bin/python3 -- Using Debian Python package layout -- Using empy: /usr/lib/python3/dist-packages/em.py -- Using CATKIN_ENABLE_TESTING: ON -- Call enable_testing() -- Using CATKIN_TEST_RESULTS_DIR: /home/dama/demo02_ws/build/test_results -- Forcing gtest/gmock from source, though one was otherwise available. -- Found gtest sources under '/usr/src/googletest': gtests will be built -- Found gmock sources under '/usr/src/googletest': gmock will be built -- Found PythonInterp: /usr/bin/python3 (found version "3.8.10") -- Using Python nosetests: /usr/bin/nosetests3 -- catkin 0.8.10 -- BUILD_SHARED_LIBS is on -- BUILD_SHARED_LIBS is on -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- ~~ traversing 1 packages in topological order: -- ~~ - hello_vscode -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- +++ processing catkin package: 'hello_vscode' -- ==> add_subdirectory(hello_vscode) CMake Error at /opt/ros/noetic/share/catkin/cmake/catkin_install_python.cmake:86 (message): catkin_install_python() called with non-existing file '/home/dama/demo02_ws/src/hello_vscode/scripts/hello_vscode_p.py'. Call Stack (most recent call first): hello_vscode/CMakeLists.txt:162 (catkin_install_python) -- Configuring incomplete, errors occurred! See also "/home/dama/demo02_ws/build/CMakeFiles/CMakeOutput.log". See also "/home/dama/demo02_ws/build/CMakeFiles/CMakeError.log". make: *** [Makefile:544:cmake_check_build_system] 错误 1 Invoking "make cmake_check_build_system" failed
06-11

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值