quick_unit
is a very clean and simple C++ Unit Test framework.
Yes… another one ;-)
It has 3 fundamental design goals:
- A single header file contains everything. No libraries or supporting C++ files needed.
- Tests must be trivially easy to create, with absolute minimum ceremony.
- Cross platform
#include "quick_unit.hpp"
DECLARE_SUITE(My First Tests)
TEST(the compiler can add) {
assert(3 == 1 + 2, SHOULD(add numbers));
}
TEST(the compiler can subtract) {
assert_equal(1 , 2 - 1, SHOULD(subtract numbers));
}
int main(int argc, char *argv[]) {
return RUN_TESTS();
}
If you don’t like the use of assert
, you can change it. See below.
The SHOULD elements are optional, but you should use them…
====================================================
Starting My First Tests at Mon Jun dd hh:mm:ss yyyy
Test: the compiler can add => OK.
Test: the compiler can subtract => FAILED. line 14: Should subtract numbers (Expected: 11, got: 1)
Test: we can output text in a test => FAILED. No assertions were executed
-- Output --
I am in test 'we can output text in a test'
I am on line 19
------------
----------------------------------------------------
Finished My First Tests at Mon Jun dd hh:mm:ss yyyy
Passes: 1 Fails: 2
----------------------------------------------------
====================================================
Starting Compiler Tests at Mon Jun dd hh:mm:ss yyyy
Test: Compiler checks => OK.
----------------------------------------------------
Finished Compiler Tests at Mon Jun dd hh:mm:ss yyyy
Passes: 1 Fails: 0
----------------------------------------------------
Just copy quick_unit.hpp
into your project.
It’s not big.
It’s not clever.
It’s just supposed to let me write unit tests without getting in the way.
I spent decades programming in C++ and didn’t know any better.
Then I found Ruby and it bent my mind. It changed completely the way I think about programming. Thanks Matz. Thanks Dave Thomas.
I now favour code clarity over clever complexity.
I insist on test first / test driven programming. Even in C++. Especially in C++!!!!!
quick_unit
is my response to having to go back to a C++ project and not being able to find a zero-impedance unit test framework.
Some came close, and I resisted for a while, but I could not find any existing code that gave me the ability to ‘just write it’. I hope that is what is achieved in quick_unit
and that it is so easy to use that no-one will have an excuse to skip their tests.
NOTE: Some people don’t like to use the word assert
in quick_unit because of possible confusion with the assert
macro in assert.h
. You can pick your own verb to use by declaring it as the value to macro QU_ASSERT, before including quick_unit.
#define QU_ASSERT verify
#define CAN QU_SHOULD
#include "../quick_unit.hpp"
// ----------------------------
DECLARE_SUITE(Verify syntax)
TEST(Experiments: verify syntax) {
verify(1, CAN(be true));
verify_true(1, CAN(be true));
verify_false(0, CAN(be false));
verify_equal(1, 2-1, CAN(be equal));
verify_not_equal(1, 2, CAN(differ));
}
assert(<truth>[, <message>])
fails the test if <truth>
is not true. The optional <message>
can be a char *
, but it is better to use the SHOULD()
macro, because that binds the message with the line number for reporting purposes.
assert_true(<truth>[, <message>])
is the same as assert(<truth>[, <message>])
assert_false(<truth>[, <message>])
fails the test if the value passed in is not false.
assert_equal(<expected>, <actual>[, <message>])
fails the test unless <expected> == <actual>
.
assert_equal
uses ==
to do the match, so custom types and classes can be compared if ==
is supported. For reporting purposes, they must also be able to emit to a std::ostream.
assert_not_equal(<expected>, <actual>[, <message>])
fails the test unless <expected> != <actual>
.
assert_equal
uses !=
to do the match, so the same rules as above apply to custom types.
Setup and teardown methods can be attached to a test suite. The setup method gets called before each test. The teardown method gets called after the test, regardless of whether the test passes or not.
A slightly expanded suite declaration is needed for this, as shown below. You can declare variables in the suite which can be accessed in its tests (as shown). The name passed to END_SUITE_AS
becomes the name that you use to access the suite variables.
BEGIN_SUITE(Setup and teardown)
Wibble *thing;
SETUP { thing = new Wibble(); }
TEARDOWN { delete thing; }
END_SUITE_AS(suite)
TEST(First test) {
assert( suite.thing->IsEmpty(), SHOULD(be new for each test));
suite.thing->Add("Boo");
assert( !suite.thing->IsEmpty(), SHOULD(not be empty));
}
TEST(Second test) {
assert( suite.thing->IsEmpty(), SHOULD(be empty because it is a new instance));
}
You can also run code to setup and teardown the complete suite. This is only rarely useful – perhaps to make a connection to some expensive resource. You should probably not be doing such things in a unit test though – consider mocking or stubbing.
BEGIN_SUITE(Setup and teardown)
DatabaseConnection connection;
SETUP_SUITE { connection.Connect(); }
TEARDOWN_SUITE { connection.Disconnect(); }
END_SUITE_AS(suite)
TEST(Something) {
... use suite.connection
}
By default, test results are reported on std::cout
. This can be changed to any other stream of type std::ostream
:
...
int main(int argc, char *argv[]) {
RUN_TESTS(); // To default std::cout
auto_ptr<ofstream> results_file(new ofstream("results.txt"));
TEST_OUTPUT(*results_file);
return RUN_TESTS(); // To file
}
If you use the Output()
stream or printf
in your test, then the text will be passed to each reporter and will take note of TEST_OUTPUT
redirections.
...
TEST(we can output text in a test) {
Output() << "I am in test '" << name() << "'" << std::endl;
printf("I am on line %d\n", __LINE__);
assert(true);
}
quick_unit
has a default reporter that prints the suite/test names to the screen, along with information about pass/fails and test duration.
It is possible to replace the default reporter with one of your own:
#include "quick_unit.hpp"
TEST_REPORTER(Netbeans)
DECLARE_SUITE(My First Tests)
TEST(...)
Alternatively you can add additional reporters, and each of them will get notified of the status:
#include "quick_unit.hpp"
ADDITIONAL_REPORTER(Netbeans)
ADDITIONAL_REPORTER(LogToMySQL)
DECLARE_SUITE(My First Tests)
TEST(...)
Tested on:
- Visual Studio 2010
- Visual Studio 2005
- MinGW GCC (5.16)
- Debian Linux
- Ubuntu
Example files are held in the repository to show examples of tests on differing platforms.
The MinGW, Ubuntu and Linux versions are built using Netbeans and emit results in the Netbeans ‘Simple Test’ format. They therefore integrate well as standalone tests within an overall C++ project.
Copyright © 2011 David Lake (rifraf at rifraf.net)
Released under the MIT license. See LICENSE file for details.