Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: How to simulate different timezones in unit tests using google test

I'm working in C++ with Google test for unit testing. In our companies code we still have the problem, that system data types as for example CTime use for example the timezone of the actual system (we use the CTime datatype the same way like in the production code). For unit testing, we want to simulate different time zones because we have many problems with that.

I already invested hours researching how others do that because it can't be, that other companies don't have this problem :) But I didn't find a solution for that. My thoughts are like this: CTime itself saves the time in UTC and converts it on demand into local time. My assumption is that all these functions need the GetTimeZoneInformation() functionality which is declared in timezonapi.h.

WINBASEAPI
_Success_(return != TIME_ZONE_ID_INVALID)
DWORD
WINAPI
GetTimeZoneInformation(_Out_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation);

So the solution would maybe be to exchange this GetTimeZoneInformation() functionality by a customized function which then returns an object which I can change on demand. But I don't know how to do that and I also do not know if this is working then or not because it's maybe only calling this function once when the application starts.

Does anyone know how to deal with this topic? I don't want to change any behaviours, I only want to be able to set my own timezone without breaking any time calculation mechanism. The best thing would be if I could somehow mock these calls without changing production code for that. The thing is that this program is very big, old, and... changing as less as possible is always the best :)

If I've not found an already existing post which solved my problem then I'm really sorry because I either didn't find it or I didn't get that this can help me.

like image 622
Lifthrasil Avatar asked Nov 08 '22 06:11

Lifthrasil


1 Answers

As you mentioned in your question, mocking is a good way to solve this problem.

The fastest and easiest way I know to that is by wrapping all the methods you want to test in a new class, which implements 'Strategy' design pattern:

It can switch between real implementation and mocks, once you set the logic to a mock, it will return a mock value.

By default it should be set to the real implementation.

Simple example with 'int Foo()':

    Class Strategy{
            int Foo() = o
    }

    Class StrategyImplementation
    {
    private:
            Strategy* logic;
    public:
            void SetLogic(Strategy* newLogic){logic=newLogic)}
            int Foo(){return logic->Foo()}
    }

    Class mock : public Strategy{
    public:
            SetValue(int value){this.value = value}
            int Foo(){return value}
    private:
            int value;
        }

    Class Implementation  : public Strategy{
        int Foo(){ here_you_call_the_method_you_want_to_test }
    }

All is left, is to change every call of GetTimeZoneInformation to the new Strategy, The easiest way to do that, would be by using 'Singleton' design pattern.

It doesn't have high risk, and the changes you will need to do is changing calls from 'Foo()' to 'StrategyImplementation::GetInstance().Foo()'

like image 120
Asafm Avatar answered Nov 14 '22 22:11

Asafm