Jump to content
  • Advertisement
  • entries
    24
  • comments
    20
  • views
    2642

C++ Set Up GMock for TDD. C++, VS

8Observer8

710 views

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 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By Belfa96
      I'm a total beginner with Directx/3D programming. I need help with implementing hardware instancing on Directx 11. I'm trying to render multiple cubes on the screen, to create some sort of Minecraft-esque voxel engine. The problem is, I don't know where to start to achieve this. This is how my "render frame" function looks:
      void RenderFrame(void) { D3DXMATRIX matView, matProjection; D3DXMATRIX matFinal; // create a view matrix D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0.0f, 9.0f, 24.0f), // the camera position &D3DXVECTOR3(0.0f, 0.0f, 0.0f), // the look-at position &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); // the up direction // create a projection matrix D3DXMatrixPerspectiveFovLH(&matProjection, (FLOAT)D3DXToRadian(45), // field of view (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio 1.0f, // near view-plane 100.0f); // far view-plane // create the final transform matFinal = matView * matProjection; // clear the back buffer to a deep blue devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f)); // clear the depth buffer devcon->ClearDepthStencilView(zbuffer, D3D11_CLEAR_DEPTH, 1.0f, 0); // select which vertex buffer to display UINT stride = sizeof(VERTEX); UINT offset = 0; devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset); devcon->IASetIndexBuffer(pIBuffer, DXGI_FORMAT_R32_UINT, 0); // select which primtive type we are using devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // draw the Hypercraft devcon->UpdateSubresource(pCBuffer, 0, 0, &matFinal, 0, 0); devcon->DrawIndexed(24, 0, 0); // switch the back buffer and the front buffer swapchain->Present(0, 0); } Notice that there's a single vertex buffer containing the verteces of a cube, and index buffer containing its indeces. I want to render many (5000+) cubes on the screen at once on a single draw call, without performance issues, so I know instancing is the way to go, but I don't know how to implement it in my code. What changes do I need to do to my code in order to display multiple instances of the cube?
      Thanks in advance!
    • By Josheir
      I am trying to visualize if the attached diagram of data of triangles is correct for the attached hill image.  I used a nested for loop to find all triangles with any vertices having a height greater than zero.  The two triangles marked with an X surprisingly have no height values and the shape of the triangles is odd to me.  The triangles marked with A are left triangles and the triangles marked with B are right triangles.  The rendering was done in OpenGL with C++, and the X coordinates and  Z coordinates are by convention.  
      The one point with a height is shown here:
       
      void addHillsManually() { heightMapFromArray[20][20] = .5; } Thank you,
      Josheir


    • By SBD
      Normally something I would follow up on in a Microsoft forum, but thought I'd drop in here first...
      I had migrated my project from Visual Studio 2010 to Visual Studio 2017 late last year, and everything went smoothly.  Just recently, however, I was reviving a testing harness I was using for network code and have been encountering a linker error that is slightly befuddling.  Specifically, only in my release builds, I get these:
      MSVCRT.lib(delete_array.obj) : error LNK2005: "void __cdecl operator delete[](void *)" (??_V@YAXPEAX@Z) already defined in MSVCRT.lib(delete_array.obj)
      MSVCRT.lib(delete_scalar.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPEAX@Z) already defined in MSVCRT.lib(delete_scalar.obj)
      Now, normally the first stop in MSVCRT linker errors is to check and make sure all the CRT code generation settings are matching across all libs (Multithreaded DLL, etc.).  I have meticulously checked ALL settings 6 times over to ensure they match.  But then, things are actually a little strange when you consider that in this particular case it's complaining that something in MSVCRT.lib is already defined in...MSVCRT.lib.  That's interesting.
      I do have my own overrides for global new and delete, and commenting out the specific ones noted in the link errors does get things linking error-free.  It is worth noting that my overrides are NOT inlined (I guess these days, it's actually not allowed).  Further, why it is *only* those two variants (not the std::nothrow_t variants), and only delete (no issues with the overridden new(s)) is a bit perplexing.  Even more, I am using said overrides/library in many other projects without this issue (and as noted, this was not an issue in this particular project back in Visual Studio 2010).
      This smells to me like a Visual Studio 2017 linker issue, but I thought I'd pop in here and see if anyone had seen something similar?
    • By a light breeze
      I'm looking for a 3D renderer (not a complete game engine) to use for my next project.  I've taken a close look at Ogre and turned away in horror.  I've taken a quick look at Horde3D, but did not get it to compile.  Are there any other (better) options?
      I have three absolute requirements:
      It must be open source, with a liberal license. It must be usable from C++. It must run on my main computer (great CPU, lots of RAM, relatively recent Nvidia graphics card, but OS is Lubuntu). Everything else is technically negotiable.  Ideally, I'd like it to have low system requirement, great performance, a clean API, no bugs, visually beautiful results.  And a pony.
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!