Set Up GMock for TDD. C++, VS

Published February 19, 2019
Advertisement

If you need the example how to set up GTest without GMock you can see this example: Set Up GTest for TDD. C++, VS

In this example: PersonService_GMockCpp.zip  we will see how to use Google Mock for creating mock objects and how to write a unit test for testing exceptions. I translated this example from the TypeScript tutorial: Using Jasmine Spies to Create Mocks and Simplify the Scope of Your Tests

You can download and run the example. Google Test library is included in the project as source folder and it is placed in "Libs" folder.

Note. If you have another version of VS then before you will run unit tests you need to select VS 2017, like in this screenshot:

Spoiler

PlatformToolset.png.3c9a59798e0e30baeee97b337b6bbe48.png

You need to:

  • open the solution. The solution is file with name: "PersonService.sln"
  • select your version of VS, for example VS 2017 instead of VS 2015 as in the screenshot above
  • make the "PersonService.UnitTests" project as "StartUp Project". For this: make right mouse button click on the "PersonService.UnitTests" project -> select "Set as StartUp Project"
  • press Ctrl+F5 to run unit tests

How to set up GTest and GMock from scratch

I use version 1.8.1 of GTest and GMock. You can download these libraries here:

You need to create a new solution. Write some name for you solution and for your project, for example: PersonService (for the solution and for the project). Pay attention, you need to check "Create directory for solution" when you create a new solution and project.

Note. RMB - Right Mouse Button click.

You can set up the project from scratch like this:

  • Create a new solution with the name "PersonService". Check "Create directory for solution". Write name "PersonService" for project. Set a new project as: empty console project, without the "precompiled headers". The "PersonService" project will be a project under test
  • Copy and add these files to the "PersonService":

IDataContext.h


#pragma once

#include "Person.h"

class IDataContext
{
public:
    virtual ~IDataContext() {};
    virtual void SavePerson(const Person &person) = 0;
};

IPersonValidator.h


#pragma once

#include "Person.h"

class IPersonValidator
{
public:
    virtual ~IPersonValidator() {};
    virtual bool IsValid(const Person &person) = 0;
};

Person.h


#pragma once

#include <string>

class Person
{
public:
    int number;
    std::string name;
};

PersonService.h


#pragma once

#include "Person.h"
#include "IPersonValidator.h"
#include "IDataContext.h"

class PersonService
{
public:
    PersonService(IPersonValidator *validator, IDataContext *dataContext);

    void Save(const Person &person);

private:
    IPersonValidator *_validator;
    IDataContext *_dataContext;
};

PersonService.cpp


#include "PersonService.h"

PersonService::PersonService(IPersonValidator *validator, IDataContext *dataContext)
{
    _validator = validator;
    _dataContext = dataContext;
}

void PersonService::Save(const Person &person)
{
    if (_validator->IsValid(person))
    {
        _dataContext->SavePerson(person);
    }
    else
    {
        throw std::runtime_error("Person is not valid");
    }
}
  • Add a new project in your solution. For this: RMB on the solution name -> "Add" -> "New Project..." -> Set a name: "PersonService.UnitTests". You project must be: console, empty and without the "precompiled headers". The "PersonService.UnitTests" project will have unit tests for the "PersonService" project
  • Create the "Libs" folder in your solution folder (where your ".sln" file is placed)
  • Copy the "gtest-1.8.1" and the "gmock-1.8.1" folders to the "Libs" folder from these archives: gtest-1.8.1.zipgmock-1.8.1.zip
  • Open the project properties of the project "PersonService.UnitTests" (RMB on the project name and select "Properties") and add these lines to "C/C++" -> "General" -> "Additional Include Directories":

$(SolutionDir)Libs\gtest-1.8.1\include
$(SolutionDir)Libs\gtest-1.8.1
$(SolutionDir)Libs\gmock-1.8.1\include
$(SolutionDir)Libs\gmock-1.8.1
$(SolutionDir)PersonService
  • Click "Apply" and "OK" buttons
  • Make "PersonService.UnitTests" as "StartUp Project". For this: RMB on the "PersonService.UnitTests" project -> select "Set as StartUp Project"
  • Add as "Existing Item" this file: "Libs\gtest-1.8.1\src\gtest-all.cc" and "Libs\gmock-1.8.1\src\gmock-all.cc". For this: RMB on the "PersonService.UnitTests" project -> select "Add" -> "Existing Item..." -> choose these files: "Libs\gtest-1.8.1\src\gtest-all.cc" and "Libs\gmock-1.8.1\src\gmock-all.cc"
  • Add as "Existing Item" the files that you will test. For example, in this case: "PersonService.cpp" from the "PersonService" project
  • Copy and add these files to the "PersonService.UnitTests":

main.cpp


#include <gmock/gmock.h>
#include <gtest/gtest.h>

int main(int argc, char **argv)
{
    testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

PersonServiceTests.cpp


#include <gmock/gmock.h>
#include <gtest/gtest.h>

using ::testing::_;
using ::testing::Return;

#include "IDataContext.h"
#include "IPersonValidator.h"
#include "Person.h"
#include "PersonService.h"

class MockDataContext : public IDataContext
{
public:
    MOCK_METHOD1(SavePerson, void(const Person &person));
};

class MockPersonValidator : public IPersonValidator
{
public:
    MOCK_METHOD1(IsValid, bool(const Person &person));
};

TEST(PersonService, IsValid_ValidPerson_CallSavePerson)
{
    MockDataContext dataContext;
    MockPersonValidator validator;
    PersonService service = PersonService(&validator, &dataContext);
    Person validPerson;

    EXPECT_CALL(validator, IsValid(_))
        .WillOnce(Return(true));

    EXPECT_CALL(dataContext, SavePerson(_)).Times(1);

    service.Save(validPerson);
}

TEST(PersonService, IsValid_NotValidPerson_ThrowException)
{
    MockDataContext dataContext;
    MockPersonValidator validator;
    PersonService service = PersonService(&validator, &dataContext);
    Person validPerson;

    EXPECT_CALL(validator, IsValid(_))
        .WillOnce(Return(false));
    EXPECT_CALL(dataContext, SavePerson(_)).Times(0);

    //EXPECT_THROW({
    //    service.Save(validPerson);
    //}, std::runtime_error);

    try
    {
        service.Save(validPerson);
        FAIL() << "Exptected std::runtime_error";
    }
    catch (std::runtime_error const & err)
    {
        EXPECT_EQ(err.what(), std::string("Person is not valid"));
    }
    catch (...)
    {
        FAIL() << "Exptected std::runtime_error";
    }
}
  • Run the "PersonService.UnitTests" project by pressing on "Ctrl+F5" buttons
  • You will see that you tests passed:

PersonService_GMockCpp.png.9a906ca66fe6f016361ce2635579ff39.png

0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement