Simple testing tool for testing individual functions or classes, as well as interactions between multiple classes.
To test a single function, an instance of the fbtt::Test
class must be initialized with a name and a function.
In this example, a lambda
-expression is used, but any storable function works (e.g. function pointer, std::function, lambda, non-static member functions).
The function must have the signature void()
.
Test factorialTest { "Factorial of 5 is equal to 120", []() {
assertEquals(factorial(5), 120);
}};
To run the test, call Test::run()
.
factorialTest.run();
To display the result of the test, either call Test::report()
or output the test to an ostream.
std::string result = factorialTest.report();
// or
std::cout << factorialTest;
This will, if the function passed, produce the following output:
If the function had failed, if factorial(5)
had been evaluated to 121
, the test result would have been:
The different types of available assertions are:
assertTrue(bool assertion, const std::string & onFail = "");
- Assert that
assertion
is trueonFail
: String to error, if assertion fails. Defaults to "".
assertTrue(ball.isBouncy, "Ball is not bouncy");
assertFalse(bool assertion, const std::string & onFail = "");
- Assert that
assertion
is falseonFail
: String to error, if assertion fails. Defaults to "".
assertFalse(dog.isBarking(), "Dog is barking");
assertEquals(T x, T y, const std::string & onFail = "");
- Assert that value
x
andy
of equality-comparaple typesT
andU
are equalonFail
: String to error, if assertion fails. Defaults to "".
// assert factorial(5) == 120
assertEquals(factorial(5), 120, "Factorial 5 is not equal to 120");
assertNoteq(T x, U y, const std::string & onFail = "");
- Assert that value
x
andy
of equality-comparaple typesT
andU
are not equalonFail
: String to error, if assertion fails. Defaults to "".
// assert 7.0 != 3.0
assertNoteq(7.0, 3.0, "Maths is broken: 7.0 is equal to 3.0");
assertThrows<ErrorType>(Function f, Args ... args);
- Assert, that
f
throws an instance ofErrorType
, when invoked with(args...)
// assert, that factorial throws instance of FactorialError, when factorial(-1) is called.
assertThrows<FactorialError>(factorial, -1);
To show an example of how a class could be tested with the fbtt::MultiTest<>
class, the following example will test the standard library std::vector
.
A MultiTest is declared by giving the test a name, and passing template parameters of the different classes, you wish to test.
Here, we are testing the empty std::vector<int>
class.
using namespace fbtt;
//...
MultiTest<std::vector<int>> emptyVectorTest;
A simple test can be added. For instance, a vectors ::resize()
and ::size()
methods can be tested.
emptyVectorTest.addTest(
"When resized to x, has size x", [](std::vector<int> & vector)
{
vector.resize(20);
assertEquals(vector.size(), 20);
}
)
As you can see, the lambda function is passed an instance of std::vector<int>
by reference.
Another function may be created. This function could be a test of an error, thrown by std::vector<int>
. If you want to test for an error, you can add an expected error to the test as a template parameter:
emptyVectorTest.addTest<std::out_of_range>(
"When referencing element -1 with ::at(), throws out of range",
[](auto & vec) {
vec.at(-1);
}
);
Because std::vector<int>
has a default constructor, an instance is constructed by default before every test. If this is not possible, (if you have a class, which has no default constructer) a constructor can be added with MultiTest::addConstructor()
emptyVectorTest.addConstructor(
"initializing with 0", [](std::vector<int> * & vec)
{
vec = new std::vector<int>(0);
}
)
If this is defined, the tests will begin with this constructor, instead of the predefined constructor. Any number of constructors can be added to the MultiTest
.
Constructors added with addConstructor
must allocate to free store with new
.
When MultiClass::run()
is called, the following happens (assume 2 constructors, 3 tests):
- The 1st constructor is called
- The 1st test is executed
- The destructor is called (
delete
on object) - The 1st constructor is called
- The 2nd test is executed
- The destructor is called
- The 1st constructor is called
- The 3rd test is executed
- The destructor is called
- The 2nd constructor is called
- The 1st test is executed
- The destructor is called (
delete
on object) - The 2nd constructor is called
- The 2nd test is executed
- The destructor is called
- The 2nd constructor is called
- The 3rd test is executed
- The destructor is called
When the MultiTest
is output to an output stream, the following output is produced:
std::cout << multiTest;
This multitest can be found in "examples/vectorTest.cpp"
.
The MultiTest::addConstructor
-method takes a function pointer as its second argument. This function pointer has the signature void(Classes * & ...)
.
This would, for a test with std::vector<int>
mean, that the signature is void(std::vector<int> * &)
. This has been done, so simple construction functions would remain simple, e.g.:
test.addConstructor(
"Simple constructor for T", [](T * & t) {
t = new T { x, y, z };
}
);