How to do unit testing using Catch/GTest (specifically in CLion)?


#1

Hi Everyone,

Currently I’ve decided to start unit testing my application and seeing that CLion has support for both Catch and GTest (Google Test), I would like to use one of those. The problem is that both actually want to create the main method themselves, as does JUCE (the START_JUCE_APPLICATION()), which is of course not possible. I’ve tried some macro-magic, where I either let JUCE or GTest create the main method, but this resulted in recompiling my entire application every time I switched, which was to say, less than ideal. Ideally, I would like to have two different configurations, one where I would simply run the program and the second one in which only the tests are ran. Is this possible? And can anyone tell me how I could use one of these frameworks in combination with JUCE?

In addition, are there strong (dis)advantages with either of them? So far, it seems as if Catch is somewhat simpler (only a single header file for example), while GTest is integrated with GMock (I don’t need mocking right now, but I might in the future).

Thanks in advance for your help!


#2

A quick google produced information on starting Catch from your own main:
Supplying main() yourself


#3

Dear cpr,

Thanks for your answer and I have indeed found several similar solutions. However, JUCE-applications also don’t have a main, as it is generated by:

//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (FractaliciousApplication)

So how would I get access to the argc and the argv that Catch is expecting?

Also, something like this will more or less “include” the tests in the normal code base, while I was wondering if there was some way to keep them as separate as possible (so the binary would also not include the tests). Something where either an executable is built doing nothing but testing and one that would be the actual program (by using targets for example?).


#4

YAG,
You can certainly reconstruct argv/argc. Assuming you are deriving from JUCEApplication, in your implemention of JUCEApplicationBase::initialise(const String& commandLineParameters), call StringArray JUCEApplicationBase::getCommandLineParameterArray(), and then use the returned StringArray to reconstruct your argv/arc lists.

For controlling the inclusion of the tests in the code, you can use a macro and add new build targets (which you already have 2 of, release and debug). So you would have release, debug, test-release, test-debug, where you could you would set the macro accordingly, INCLUDE_TESTS=0(or 1), for example. and surround all the test related code with #if INCLUDE_TESTS/#endif blocks.


#5

Thank you very much cpr, I did make some progress and got Catch to work (somewhat) nicely with CLion

You can certainly reconstruct argv/argc. Assuming you are deriving from JUCEApplication , in your implemention of JUCEApplicationBase::initialise(const String& commandLineParameters) , call StringArray JUCEApplicationBase::getCommandLineParameterArray() , and then use the returned StringArray to reconstruct your argv/arc lists.

For anyone running into similar problems, you also have to prepend the program name itself to argv, as JUCEApplicationBase::getCommandLineParameterArray() automatically strips that! Without it, CLion does not seem to understand that tests are being ran and keeps showing “instantiating tests…”.

For controlling the inclusion of the tests in the code, you can use a macro and add new build targets (which you already have 2 of, release and debug). So you would have release , debug , test-release , test-debug , where you could you would set the macro accordingly, INCLUDE_TESTS=0(or 1) , for example. and surround all the test related code with #if INCLUDE_TESTS/#endif blocks.

Yeah, originally I did something along the lines of (the macro-magic I was referring to):

#if INCLUDE_TESTS == 1
START_JUCE_APPLICATION (Application) //Let JUCE create the main
#elif
int main(int argc, char **argv) { //Create a main calling GTest
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
}
#endif

But like that, this also causes my entire project to be recompiled every time I switch from debug to testing, which is taking ages. Is there no way around this? Because my heart is really not able to keep up with a coffee break every fifteen minutes…