wiki:WritingTests
Last modified 2 years ago Last modified on 2012-03-15 21:33:55

We use the CxxTest framework for tests. See the CxxTest user guide for an introduction and general reference.

Tests are highly recommended for new code, and for bug fixes to old code, because they provide some assurances of quality and help detect regressions and portability issues. Sometimes code might need to be redesigned a bit so that it's more easily testable, which is fine.

There are a few issues specific to our game:

Test locations

The tests for file foo/bar/Baz.{cpp,h} usually go into foo/bar/tests/test_Baz.h, and have the following form:

/* Copyright (C) 2009 Wildfire Games.
 * This file is part of 0 A.D.
 *
 * 0 A.D. is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 0 A.D. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "lib/self_test.h"

#include "foo/bar/Baz.h"

class TestBaz : public CxxTest::TestSuite 
{
public:
    void test_whatever()
    {
        ...
    }
};

After adding a new test file, you have to run the update-workspaces script to integrate the new file into the build system.

New assertion macros

TS_ASSERT_OK(expr) -- assert expr == INFO::OK.

TS_ASSERT_STR_EQUALS(str1, str2) -- assert std::string(str1) == std::string(str2).

TS_ASSERT_WSTR_EQUALS(str1, str2) -- assert std::wstring(str1) == std::wstring(str2).

TS_ASSERT_STR_CONTAINS(str1, str2) -- assert str2 is contained somewhere within str1.

TS_ASSERT_VECTOR_EQUALS_ARRAY(vec1, array) -- assert a std::vector is equal to a constant-sized array.

Mocks

Particularly for testing code that relies on global system functions (time, getcwd, etc), it can be useful to replace the functions with mock objects that return special values or do special checks on their arguments. The CxxTest guide describes how to do that. We use the following specifics:

  • To mock functions from foo.h, create the file mocks/foo.h saying:
    #include <foo.h>
    #include <cxxtest/Mock.h>
    CXXTEST_MOCK_GLOBAL(...)
    CXXTEST_MOCK_GLOBAL(... etc, as in the CxxTest guide ...)
    
  • Update mocks/mocks_real.cpp to add the line #include "mocks/foo.h"
  • Update mocks/mocks_test.cpp to add the line #include "mocks/foo.h" and, if you want to default to calling the real function (instead of an empty dummy function) in tests that don't override it then add DEFAULT(foo);
  • In any source file that should use the mocks, do a #include "mocks/foo.h" (instead of the original #include <foo.h>), and use T::func() instead of func().

Running tests

On Windows: Run the "test" project in the VS debugger. Or, use "set as startup project" on the "test" project and then use F5 to run it.

On Linux and OS X: Run make or make test to build the test executable. Then run ./test_dbg from binaries/system.

To run a single test suite instead of all tests, add the command-line argument -test TestSuitename. To run a single test, add -test TestSuitename::test_case_name (using the class/method names from the test code).