Skip to content

A tiny unit testing framework for C++. Single header file, clean syntax. Inspired by Ruby.

License

Notifications You must be signed in to change notification settings

rifraf/quick_unit

Repository files navigation

Overview

quick_unit is a very clean and simple C++ Unit Test framework.

Yes… another one ;-)

It has 3 fundamental design goals:

  1. A single header file contains everything. No libraries or supporting C++ files needed.
  2. Tests must be trivially easy to create, with absolute minimum ceremony.
  3. Cross platform

Example

#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…

Sample output

====================================================
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
----------------------------------------------------

Installation

Just copy quick_unit.hpp into your project.

Excuses

It’s not big.

It’s not clever.

It’s just supposed to let me write unit tests without getting in the way.

Motivation

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.

Advanced features

Assertions

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

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

assert_true(<truth>[, <message>]) is the same as assert(<truth>[, <message>])

assert_false

assert_false(<truth>[, <message>]) fails the test if the value passed in is not false.

assert_equal

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

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.

Test setup and teardown

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));
}

Suite setup and teardown

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
}

Output stream

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);
}

Reporters

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(...)

Platforms

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.

License

Copyright © 2011 David Lake (rifraf at rifraf.net)

Released under the MIT license. See LICENSE file for details.

About

A tiny unit testing framework for C++. Single header file, clean syntax. Inspired by Ruby.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published