Skip to content

A minimalist unit test framework for C++11 presented as a single file

License

Notifications You must be signed in to change notification settings

evolutional/upptest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 

Repository files navigation

About

µTest is an ultra-lightweight, minimalist unit test framework for C++11. It is the sister-framework of µTest for C99.

Compiling

µTest is provided as a single-header library. To compile, you simply include the header where you need to and ensure that you provide the implementation by defining UTEST_CPP_IMPLEMENTATION in exactly one source file before you include the header.

	#define UTEST_CPP_IMPLEMENTATION
	#include "upptest.h"

There are no external dependencies required. The main build requirement is to have C++ exception handling enabled.

Usage

Implementing Tests

This is a simple example of how to implement a basic test in µTest.

TEST(ExampleFailingEqualityTest, "Example.Tests")
{
	utest::assert::neq(50, 50);
}

Each test is declared with a name ('ExampleFailingEqualityTest') and a category ('Example.Tests').

Tests follow the xUnit pattern, with each test being encapsulated in its own class. When declaring tests, it is recommended you declare them in a .cpp file as they will automatically register themselves with the engine.

Assertion

µTest provides several simple assertion template functions for use in tests. Is an assert condition fails, a test will terminate immediately, throwing a utest::assert_fail_exception which is handled by the execution engine.

Example:

utest::assert::eq(50, 50);

In order to provide information about the file and line the assertion failed, several convenience macros have been provided. These have the name UASSERT_*.

The basic assert actions are are:

function macro required operator on type T
assert::eq UASSERT_EQ T == T
assert::neq UASSERT_NEQ T != T
assert::is_true UASSERT_TRUE if (T)
assert::is_false UASSERT_FALSE if (!T)
assert::is_null UASSERT_NULL T == nullptr
assert::is_not_null UASSERT_NOT_NULL T != nullptr
assert::fail UASSERT_FAIL N/A
assert::expr UASSERT if (expr)

If you are wishing to use assert::eq or assert:neq, you must currently provide the following operator to emit a friendly assert message.

inline std::ostream& operator << (std::ostream& os, const MyType& v)
{
	// stream your type to 'os' here
	// eg: os << static_cast<std::underlying_type<MyType>::type>(v);
	return os;
}

Executing Tests

Executing the tests can be performed in several ways. The simplest is to run all tests that are registered:

auto res = utest::runner::run_registered([](const auto& tst){
	// observer fired after completion
	// you have full info about the test here
	// including status, error message, execution time, etc
});

µTest features no built-in test result reporters, instead you provide your own observer function that is called after each test has been executed.

An example that runs all test and prints the results to std::cout is:

auto res = utest::runner::run_registered([](const utest::result& tst){
	std::cout << "'" << tst.info->name << "' executed in "
		<< tst.duration.count() << "ms with result: ";
	if (tst.status == utest::status::pass)
		std::cout << "pass" << std::endl;
	else
		std::cout << "failed" << std::endl;
});

Filtering

Tests can be executed with a filter predicate which will be passed a const utest::info* const for evaluation.

An example which filters tests in a specific category:

auto res = utest::runner::run_registered([](auto ti) {
	return std::string(ti->category) == "Example.Tests"; 
	},	
	[](const auto&) {}
);

All tests are registered with a global utest::registry. You can retrieve these tests by accessing:

const auto& tests = utest::registry::get()->tests();

You can use this to fill your own stl containers and then execute them with utest::runner::run(). The run function has various overrides that accepts collections, begin/end range iterators and filter predicates, allowing you to easily create your own filtering and execution process.

Fixtures

µTest supports fixtures by allowing tests to share a common base class and category. This allows you to implement complex tests that share common logic.

Fixtures are declared as follows:

TEST_FIXTURE(MyFixture)
{
public:
	MyFixture()
		: expected(100)
	{		
	}

	int expected;
};

There's nothing special about the macro, it simply declares a class that inherits utest::test.

From here, you declare fixture tests as follows:

TEST_F(PassingEqualityTest, MyFixture, "MyFixtures.Tests")
{
	utest::assert::eq(expected, 100);
}

FAQ

Will µTest support feature X from {insert popular library}?

Highly unlikely. µTest created with minimalism in mind. However, if you find yourself implementing a useful, core feature feel free to contribute a pull request.

Why should I use µTest over {insert popular library}?

µTest was born out of a need to embed a tiny test framework in a project and disable it with a preprocessor define.

Why public domain?

See stb's FAQ for a great set of reasons. µTest is completely free for you to use in any way you see fit. Attribution is not expected, but it is appreciated.

What parts of C++11 does µTest use?

The following STL headers are used: chrono, exception, functional, memory, sstream, string, vector

In addition to this, the code uses C++ features such as auto and enum class.

What's the future for µTest?

µTest is essentially feature complete. It may be updated with a richer set of assert functionality should the need arise.

License

µTest is free and unencumbered software released into the public domain. See UNLICENSE for details.

About

A minimalist unit test framework for C++11 presented as a single file

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages