Ugly Stool Rotating Header Image

July, 2011:

My First C++: CMake, GoogleTest, and GoogleMock

When starting a new project I always consider how to build the code, and how to test it.  I can puzzle through Make after a fashion, but I am not a fan.  If the project is only going to exist on Windows I stop at Visual Studio and MSBuild.  If the project is ever going to run on Windows and/or Linux then start with a build system that provides portability.

CMake can generate different project files based on the OS and user desire.  For example, on Windows you can generate Visual Studio projects files (2008, 2010, etc), or NMake makefiles.  Google started a project call GYP as an alternative to CMake specifically for Chromium.  (See GYP’s wiki for a GYP vs. CMake opinion.)

With testing I am very familiar with the frameworks available on .NET and Java, but not for C++.  When I recently started a new C++ project to solve those ubiquitous block letter games on smartphones I wanted to know how I was going to test it.  After consulting the oracle I decided upon GoogleTest and GoogleMock.

It was a wee pain to figure out how to get everything to work together because I was (and still am) a CMake neophyte. 

Step 1: Create a program that needs to be tested.  I created a simple example (minus most of the implementation) that extracts links from an HTML document.

Step 2: Files

http_fetch.h defines a class that issues an HTTP GET of the specified URI and returns the contents of said URI as a string.

#ifndef __HTTP_FETCH_H__
#define __HTTP_FETCH_H__
#include <string>
class HttpFetch {
public:
    virtual ~HttpFetch() {}
    virtual std::string GetUriAsString(const std::string& uri) const {
        // TODO(cboumenot): implement
        return std::string();
    }
};
#endif // __HTTP_FETCH_H__

html_parser.h defines a class that parses the specified URI, and extracts all of the links found in the fetched HTML document.  It uses HttpFetch to get the data.

#ifndef __HTML_PARSER_H__
#define __HTML_PARSER_H__
#include <string>
#include <vector>
#include "http_fetch.h"
class HtmlParser {
public:
    HtmlParser(const HttpFetch &http) :
        _http(http) {}
    std::vector<std::string> GetAllLinks(const std::string& uri) const {
        // TODO(cboumenot): implement
        return std::vector<std::string>();
    }
private:
    HttpFetch _http;
};
#endif // __HTML_PARSER_H__

I wrote a test for the HtmlParser using GoogleMock and GoogleTest.  I use mocking to inject data into HtmlParser and assert its responses.  Just like .NET/Java mocking goodness.

#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "http_fetch.h"
#include "html_parser.h"
using ::testing::Return;
class HttpFetchMock : public HttpFetch {
public:
    MOCK_CONST_METHOD1(GetUriAsString, std::string(const std::string& uri));
};
TEST(HtmlParser, OneLink) {
    char *html = "<html>"
    "<head></head>"
    "<body>"
    "<a href='/index.html'>index.html</a>"
    "</body>"
    "</html>";
    HttpFetchMock mock;
    HtmlParser parser(mock);
    EXPECT_CALL(mock, GetUriAsString("http://example.net"))
        .WillOnce(Return(std::string(html)));
    std::vector<std::string> links = parser.GetAllLinks("http://example.net");
    EXPECT_EQ(1, links.size());
    EXPECT_STREQ("http://example.net/index.html", links[0].c_str());
}
TEST(HtmlParser, NoData) {
    char *html = "";
    HttpFetchMock mock;
    HtmlParser parser(mock);
    EXPECT_CALL(mock, GetUriAsString("http://example.net"))
        .WillOnce(Return(std::string(html)));
    std::vector<std::string> links = parser.GetAllLinks("http://example.net");
    EXPECT_EQ(0, links.size());
}

A mock is defined by deriving from HttpFetch.  All mocked methods are made public (yes, C++ allows this).  Methods are defined using GoogleMock macros.  The GoogleMock documentation is great, and has loads of information.  Everything I remember doing with NMock/EasyMock I have accomplished with GoogleMock.  I would even venture that one can do more with GoogleMock.

A test is defined with the TEST macro, and are written as one would expect.  Expectations are made on the mock before any methods on the mock are called.  Assertions are made using the EXPECT_* macros.

Building these codes starts with creating a CMake build file called CMakeLists.txt.

## CMakeLists.txt
option(foo_build_tests "Build all of foo's unit tests." OFF)
project(foo)
cmake_minimum_required(VERSION 2.6.2)
include_directories("/usr/local/include")
link_directories("/usr/local/lib")
set(foo_SOURCES
    main.cc
    html_parser.h
    http_fetch.h)
add_executable(foo ${foo_SOURCES})
if (foo_build_tests)
    enable_testing()
    add_executable(html_parser_test
        html_parser_test.cc
        html_parser.h
        http_fetch.h)
    target_link_libraries(html_parser_test
        pthread)
    target_link_libraries(html_parser_test
        gmock
        gmock_main)
    target_link_libraries(html_parser_test
        gtest
        gtest_main)
    add_test(html-tests html_parser_test)
endif()

This recipe starts of by defining a build option called foo_build_tests.  When CMake is invoked by default it will not build the tests because this option defaults to OFF.  This can easily by changed by flipping this switch when CMake is invoked.

$ cmake –Dfoo_build_tests .

By default CMake will generated makefiles, so the only thing necessary to build the code is to run make.  Of the many nice things CMake does it also adds a dependency on the CMakeLists.txt file.  If this file is out of date with respect to the Makefile, the Makefile will be re-auto-generated.  There is no need to keep invoking cmake after every change to CMakeLists.txt.

$ make

The recipes sets the project name using the project() function.  It also specifies a minimum CMake version.  If one is not defined CMake will complain, and 2.6.2 is the current default for GoogleTest.

The recipe hard-codes include and link directories (/usr/local/{include,lib})because this is where I installed GoogleMock and GoogleTest.  (A better approach would have been to use the find_package() function.)

The main project executable is defined by the add_executable() function and the list of sources that make it up (.h and .cc).

The test, html_parser_test, is defined in a much more complicated manner.  The test is only one .cc file (that does not define a main()), and the appropriate header files.  But, this test requires the GoogleMock and GoogleTest libraries as well as the dependencies of these libraries of which there is only pthread.  The dependencies of an executable can be built up, and do not have to be defined all at once.  Use the function target_link_libraries() to make the invdividual dependencies much more explicit  (or not, your call).  A better approach to defining the test would be to write a CMake function or macro that handled automatically adding the dependencies.  See the GoogleTest source code for such an example.

Finally, the function enable_testing(), and add_test() tell CMake to generate a target called test, that can be invoked easily from the command line.

$ make test

Viola!  A sample prototype for new C++ projects.

Page optimized by WP Minify WordPress Plugin