RudeMocks/Introduction

From Wiki

Jump to: navigation, search

Contents

Table of Contents

Introduction

RudeMocks is a mocking framework for C++ that allows you to easily create mock objects and set up expectations on them using a record & replay model by simply calling functions on the mock objects. It is inspired by C# mocking frameworks, such as Rhino Mocks and TypeMock.NET. The goal of RudeMocks is to nearly eliminate the need to write mocks by hand when writing unit tests. It is a useful library for test-driven development in C++ and makes writing unit tests almost as easy as in Java or C#. To the best of my knowledge, this is the first mocking framework of this kind available in C++.

The name RudeMocks comes from the fact that the implementation uses inline assembly, which in some C++ circles is considered a bit rude. The use of some inline assembly makes the implementation dependent on the compiler and the CPU architecture.

RudeMocks is available under the open source zlib/libpng license and can be used freely in commercial and non-commercial products.

The main RudeMocks website is http://rudemocks.sourceforge.net.

Features

  • Easily create mocks for
    • Classes with single inheritance.
    • Classes with multiple inheritance.
    • Classes with virtual inheritance (with the caveat that an original object must be provided for mocking purposes).
    • Free functions, static member functions, virtual member functions, non-virtual member functions.
  • Flexible, per-argument expectations
    • Natural syntax to set up expectations (just call the functions on the mocked object as usual).
    • Use operator == to check for equality, fall back to memcmp, or provide your own argument verifiers.
    • Ignore specific arguments or all arguments.
    • STL container and Boost.Range constraints [not yet].
    • Custom, user-provided expectation verification of whole function calls and/or specific arguments.
  • Classes need not be default constructible (although a default value for classes used as return value types needs to be provided) .
  • Easily integrate with Boost.Test.
  • Fully unit tested.

A First Example

Here's a quick, contrived example that creates and uses a mock for a very simple calculator class:

#include <RudeMocks/RudeMocks.h>
#include <iostream>
 
using namespace RudeMocks;
 
// A simple class that we want to mock. It could have virtual and non-virtual
// member functions.
class Calculator
{
public:
    Calculator()
        :    m_value(0)
    {}
 
    int GetValue() const { return m_value; }
    void Add(int value) { m_value += value; }
    void Subtract(int value) { m_value -= value; }
    void Multiply(int value) { m_value *= value; }
    void Divide(int value) { m_value /= value; }
 
private:
   int m_value;
};
 
void FunctionUnderTest(Calculator* calculator)
{
    std::cout << "((5 * 2) - 2) / 2 ";
 
    calculator->Add(5);
    calculator->Multiply(2);
    calculator->Subtract(2);
    calculator->Divide(2);
 
    if (calculator->GetValue() == 4)
        std::cout << "== 4! Yay, correct!" << std::endl;
    else
        std::cout << "!= 4! Ooops, not correct!" << std::endl;
}
 
void Test()
{
    // Create the mock repository and the mock for the class.
    MockRepository mocks;
    Calculator* calculator = mocks.CreateClassMock<Calculator>();
    RegisterFunctions(calculator, &Calculator::GetValue, &Calculator::Add, &Calculator::Subtract, &Calculator::Multiply, &Calculator::Divide);
 
    // Set up expectations.
    calculator->Add(5);
    calculator->Multiply(2);
    calculator->Subtract(2);
    calculator->Divide(2);
    ExpectCall(calculator->GetValue()).Return(4);
 
    // Switch to replay mode.
    mocks.Replay();
 
    // Execute code under test. Here we simply call our functions.
    FunctionUnderTest(calculator);
 
    // Verify all mocks.
    mocks.Verify();
}

The First Example Explained

This small and contrived example shows off most of the important features of RudeMocks. Let me walk you through this example. You'll find more details later in the other pages of this documentation.

  • First you need a MockRepository, which is the main entry point to the framework. If you've used Rhino Mocks you should be intimately familiar with this step.
  • Creating a mock
    • Creating a mock is done via a factory template function of the MockRepository class. You pass in the type of the mock that you want as template argument.
    • The Create function returns a pointer to the mocked object. The lifetime of the returned object is bound to the lifetime of the MockRepository.
    • The mock is initially in record mode. Any calls made to any of its member functions will set up expectations.
  • Set up expectations
    • Expectations are set up simply by calling the actual functions in case of functions that return void or by wrapping the function call in ExpectCall() and then specifying the return value that the mock should return when called later on in replay mode.
    • The actual functions don't execute because you're calling them on a mocked object.
    • When done setting up expectation switch the mock repository and with it all the created mocks to replay mode.
  • Replay mode
    • This is where you execute the code you want to test. It's the meat of your unit test.
    • In replay mode the mock replays and verifies the set up expectations as much as possible.
    • Again, functions are called as usual. However, mocked functions go to the mocked object and do not perform their actual duty. Instead, arguments are verified and matched against the set up expectations and the return values specified via Return() in record mode are returned.
  • Finally, Verify() verifies expectations that couldn't be immediately verified during replay.

Restrictions

  • Due to its low-level implementation that depends on target platform and compiler, RudeMocks is currently only supported with the Microsoft Visual C++ 8.0 compiler on the x86 architecture.
  • Mocked functions need to be registered. Calling any non-registered functions on a mock object causes undefined behavior (it will most likely crash).
  • RudeMocks is inherently thread-unsafe and due to its low-level implementation (e.g. patching functions via self-modifying code) it will probably never become thread-safe. Therefore, you can only ever have one instance of the MockRepository class at any given time.
  • RudeMocks will most likely not work for statically bound member functions or free functions in an optimized release build with function inlining turned on. Inlined functions cannot be patched and thus cannot be mocked. Using compiler-specific flags, pragmas, and extensions this problem can be somewhat alleviated but never fully eliminated. Virtual functions are rarely inlined by a compiler (although it is possible if the compiler can statically infer the type of the called object), so they can usually be mocked even in highly optimized release builds.

Acknowledgments

  • I am greatly indebted to Ayende Rahien's Rhino Mocks C# mocking framework, which opened my eyes to see how easy it can be to create mocks without the tedious work of writing them by hand. Get Rhino Mocks from his webpage.
  • Thanks also to my co-worker Rory Driscoll for fruitful discussions and who also brought my attention to various Visual C++ and GCC compiler flags that allow hooking into functions (which RudeMocks didn't use initially, but they are now offered as separate build configurations).
Personal tools