Simplex Update #2: Unit-Testing

Original Author: Francisco Tufró

NOTE: If you want to read this post with syntax highlight I strongly suggest you read it on my blog: code coverage reports.

Implementation

To avoid starting from scratch and implement my own unit testing framework, I decided to use Google’s C++ Testing Framework because:

  • It detects your tests automatically (you don’t have to maintain a list of tests, just add the cpp and you’re done).
  • It has test selection from command line built in, which is a huge time saver.
  • It exports to XML (which I intend to use with Jenkins, you’ll learn about this in the next update).
  • It says it’s easily expandable, with custome predicates and advanced features, but I’ve not used these yet.

In order to be able to actually run these tests, we need a test runner application.

Since Simplex Engine is being developed in a modular way, I wanted the tests to be as independent as possible, so I created a module for running them.

I did something that is not great here, but I found it convenient. I created a module that includes a main() function to run tests.

In this way, it’s just a matter of including the cpp files (Tests + Code to be tested) and linking simplex-test to create an executable that will run all the tests without any plumbing on the module being tested.

Since the testing framework is not supposed to be shipped with the actual game’s executable, I think it’s ok to do this.

We’ll see if I regret about this in the future, but we’re agile, aren’t we?

Take a look at Runner.cpp (Runner.h has no interesting information at all):

[cpp]

#include “Simplex/Test/Runner.h”

namespace Simplex

{

namespace Test

{

Runner::Runner (int argc, char **argv)

{

testing::InitGoogleTest(&argc, argv);

}

int Runner::Start ()

{

return RUN_ALL_TESTS();

}

}

}

int main ( int argc, char **argv )

{

Simplex::Test::Runner testRunner = Simplex::Test::Runner ( argc, argv );

return testRunner.Start ();

}

[/cpp]

What do we see here? The Runner class is used to initialize Google’s C++ Testing Framework and to run all the tests when Start is called.

Then our main function creates the runner and starts testing.

So, whenever I link to simplex-test as a library, this main function will be called, and all my tests will run.

Sample test

In order to show my workflow on unit testing, I’ll guide you through the implementation of a class. In this case I chose Adapter, a base support class to implement the Adapter pattern, which I’ll use to interface with third party libraries.

While developing the enginle I’m using TDD, but for clarity I’ll avoid the whole write test / fail / implement / pass / refactor cycle.

I’ll just show you the tests and explain a little bit about them ( NOTE: I’ll paste the full source code for the class and the tests at the end of the post ).

The first thing we need to do is to include the necessary headers:

[cpp]

#include <Simplex/Test.h>

#include <Simplex/Core/Adapter.h>

#include “AdapteeMock.h”

[/cpp]

Simplex/Test.h includes the necessary stuff from Google’s C++ Testing Framework, then I include the .h for the Adapter class, and finally a mock class.

I use this mock class in order to simulate how I’ll use the Adapter class, subclassing it and creating specific behaviour.

Now let’s check a test.

[cpp]

TEST ( SimplexCoreAdapter, AcceptsAnAdaptee )

{

AdapteeMock* adaptee = new AdapteeMock();

Adapter adapter = Adapter();

adapter.SetAdaptee( adaptee );

ASSERT_EQ ( adaptee, adapter.GetAdaptee() );

}

[/cpp]

Let’s dissect this:

All tests in Google’s C++ Testing Framework start with a call to the macro TEST.

You need to pass two parameters to TEST:

  • A test case name: this would be the group this test is part of, in this case we’re testing Simplex::Core::Adapter, so it makes sense to name the group SimplexCoreAdapter.
  • A test name: this has to be as descriptive as possible, try to identify the only thing this test does and write that. In this case we want to try that it accepts an Adaptee, so AcceptsAnAdaptee makes sense.

Implementation-wise, I like to think of a unit test as a sequence of three operations: Setup, Act, Assert. This is reflected on how I split the lines of code inside the test body creating a mental division that aids locating each section of the test.

On the first two lines of the test, I basically create a mock adaptee (which we’ll take a look at later) and an adapter (Setup).

Then, we call SetAdaptee on adapter, passing the adaptee as the only parameter (Act).

Finally, I assert that the adapter registered the adaptee calling GetAdaptee and comparing the result with the one I’ve created.

This test may sound a litte dull, but it’s exactly what I want to achieve on my Adapter class, I want it to be able to receive an adaptee.

Remember this is a base class, and will be subclassed later with more specific things in mind (For example, OpenGL will be abstracted behind a Graphics API Adapter ).

The rest of the tests have a similar structure.

The code that passes the tests

These two methods are really simple, just a getter and a setter:

[cpp]

void Adapter::SetAdaptee ( Adaptee* adaptee )

{

mAdaptee = adaptee;

}

Adaptee* Adapter::GetAdaptee ()

{

return mAdaptee;

}

[/cpp]

These two methods are all the code I need to pass this test (besides the class declaration, that is).

So.. the only thing we need now is a way to run these tests automatically.

CMake modifications

——————-

We saw a little bit about this in the previous update, but now I wanted to talk about it in context.

In order to get all the tests we use the same technique that we used for the module source.

Inside the module’s CMakeLists.txt we need to assign all the tests cpp’s to a variable:

[cpp]

file ( GLOB TESTS_CODE

${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp

)

[/cpp]

and then add an executable with that code:

[cpp]

add_executable ( SimplexCoreTests ${TESTS_CODE} )

[/cpp]

After doing this, we have to link the executable to simplex-test:

[cpp]

target_link_libraries ( SimplexCoreTests simplex-core ${TEST_LIBRARIES} )

[/cpp]

The last step is registering the executable as a test to CMake.

[cpp]

add_test ( SimplexCoreTests SimplexCoreTests )

[/cpp]

Yes, I know, there is a magic ${TEST_LIBRARIES} in there, that variable is in the root CMakeLists.txt since it’s platform dependent.

On Linux, for example, we need to add pthread as well as simplex-test, so it looks like:

[cpp]

set ( TEST_LIBRARIES simplex-test pthread )

[/cpp]

And that’s it.

When you run cmake and make, you’ll end up having an executable called SimplexCoreTests that will run all the tests for the Core module.

Not only that, since we registered the test in CMake, now we can do make tests and all the registered tests will run. Neat.

Conclusion

As of today, Simplex Engine is using Google’s C++ Testing Framework, and a clear workflow to define and run tests has been implemented.

When you create a test file (ClassName_Tests.cpp) inside the tests folder of a module, it gets automatically added to that module’s test suite.

You can start writing tests right away, and also implementing the classes it tests.

I’ve found it to be a really good setup to do TDD for now.

Full source

If you want to see the whole picture, you can take a look at the actual source code for the files I’m talking about here:

simplex-engine/CMakeLists.txt

simplex-test/src/Runner.cpp

simplex-core/CMakeLists.txt
simplex-core/include/Simplex/Core/Adapter.h
simplex-core/include/Simplex/Core/Adaptee.h
simplex-core/src/Adapter.cpp
simplex-core/tests/Adapter_Tests.cpp
simplex-core/tests/AdapteeMock.h