Question

I'm using Visual Studio 2013 Unit Testing. My code is using time() function to generate some object names and thus it is hard to ensure consistent behavior when testing.

If it was C#, I could use shims as shown in the article http://msdn.microsoft.com/en-us/library/hh549175.aspx "Getting started with shims" section.

Is there any technique how to redefine time() calls during my C++ unit test?

Was it helpful?

Solution

What you want to do is called mocking (http://en.wikipedia.org/wiki/Mock_object).

Personally I like to use Hippomocks (https://github.com/dascandy/hippomocks) which allows in the current version to mock C and C++ functions (with the notable exception of non-virtual class methods, see for example Mocking non-virtual methods in C++ without editing production code?).

OTHER TIPS

You can't "redefine" these things per se, but you can provide an alternative implementation under a different name, and make the distinction almost transparent in your real code.

Typically, you mock the function.

CxxTest takes the approach of having you invoke T::time() instead of std::time() (which is close enough!); with macro/include tricks, that call will resolve either to std::time() or to whatever replacement implementation you provide.

Here's a much-simplified, incredibly naive example designed only to demonstrate the basic principle:

foo.h

#include "mocked-functions.h"

/**
 * Takes the current time as a UNIX timestamp, and divides it by 10.
 */
inline int foo()
{
   return T::time(NULL) / 10;
}

main.cpp

#include "foo.h"
#include <iostream>

int main()
{
    std::cout << foo() << std::endl;
}

test.cpp

#define USE_MOCKS
#include "foo.h"
#include <cassert>

int main()
{
   mock_time_result = 50;
   assert(foo() == 5);

   mock_time_result = 400;
   assert(foo() == 40);
}

mocked-functions.h

#include <ctime>

#ifdef USE_MOCKS
   #include "mock-time.h"
#endif

namespace T {
   time_t time(time_t* ptr)
   {
      #ifdef USE_MOCKS
         return Mock_time(ptr);
      #else
         return std::time(ptr);
      #endif
   }
}

mock-time.h

#include <ctime>

time_t mock_time_result = 0;

time_t Mock_time(time_t* ptr)
{
   if (ptr) *ptr = mock_time_result
   return mock_time_result;
}

To run

$ g++ main.cpp -I. -o main
$ g++ test.cpp -I. -o test
$ ./main   # output is the real time div by 10
$ ./test   # output is nothing; all assertions succeed

If you don't include ctime or time.h then you may write whatever definition you want for time()

Unit testing requires deterministic code. std::time(), by definition, is non-deterministic. This leaves you with 2 options: 1) Change how you are generating names to be deterministic, or 2) mock std::time().

It seems your focus is on #2, so one of the easiest ways to do that is to wrap the call to std::time() behind your own function for name generation.

std::string generate_name(bool use_time = true)
{
    ...
    if (use_time)
    {
        // do something with time()
    }
    else
    {
        // return a value that is deterministic
    }
}

Your unit tests will pass in false for the use_time parameter.

You can avoid including <ctime> in your unit test code, and write your own version of time() to call for the unit tests (this is actually closer to the approach most TDD developers prefer).

Since you are not testing the functionality of time, but rather how you treat its output, an even better approach (the first option) would be to pass the output of time into the function you use to generate names. This way, your unit tests can control the input and test the output, completely.

std::string generate_name(unsigned long long id)
{
    // do something to generate the name for the id
}

...

// production code
std::string name = generate_name(static_cast<unsigned long long>(std::time(NULL)));

// your unit test code could look like this
std::string name = generate_name(1ULL);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top