Skip to main content

Unit testing with xtd.tunit (🟡 Intermediate)

How to create a simple unit test application using xtd.tunit, and how it compares to Google Test and Catch2.

We will validate several properties of a simple integer collection: std::vector<int> items = {1, 3, 5, 4, 2}; Validations performed :

  • Check that the collection size equals 5.
  • Verify that it contains the elements 1, 2, 3, 4, and 5 (regardless of order).
  • Verify that it contains the items 2 and 4.
  • Verify that it does not contain 6.
  • Verify that the collection is ordered.

With Google Tests​

#include <vector>
#include <algorithm>
#include <gtest/gtest.h>

namespace collection_tests {
class vector_tests : public ::testing::Test {
protected:
std::vector<int> items = {1, 3, 5, 4, 2};
};

TEST_F(vector_tests, items_size_equals_5) {
EXPECT_EQ(items.size(), 5);
}

TEST_F(vector_tests, items_are_equivalent_to_1_2_3_4_5) {
std::vector<int> expected = {1, 2, 3, 4, 5};
auto sorted_items = items;
std::sort(sorted_items.begin(), sorted_items.end());
EXPECT_EQ(sorted_items, expected) << "Expected equivalent collections {1, 2, 3, 4, 5}, but got {1, 3, 5, 4, 2}";
}

TEST_F(vector_tests, items_contains_2_and_4) {
for (int v : {2, 4})
EXPECT_NE(std::find(items.begin(), items.end(), v), items.end()) << "Expected to find " << v << " in collection.";
}

TEST_F(vector_tests, items_does_not_contain_6) {
EXPECT_EQ(std::find(items.begin(), items.end(), 6), items.end()) << "Expected collection not to contain 6.";
}

TEST_F(vector_tests, items_is_ordered) {
EXPECT_TRUE(std::is_sorted(items.begin(), items.end())) << "Expected ordered, but got {1, 3, 5, 4, 2}";
}
}

// This code produces the following output :
//
// [==========] Running 5 tests from 1 test suite.
// [----------] Global test environment set-up.
// [----------] 5 tests from vector_tests
// [ RUN ] vector_tests.items_size_equals_5
// [ OK ] vector_tests.items_size_equals_5 (0 ms)
// [ RUN ] vector_tests.items_are_equivalent_to_1_2_3_4_5
// [ OK ] vector_tests.items_are_equivalent_to_1_2_3_4_5 (0 ms)
// [ RUN ] vector_tests.items_contains_2_and_4
// [ OK ] vector_tests.items_contains_2_and_4 (0 ms)
// [ RUN ] vector_tests.items_does_not_contain_6
// [ OK ] vector_tests.items_does_not_contain_6 (0 ms)
// [ RUN ] vector_tests.items_is_ordered
// /!---OMITTED---!/unit_test_project1/src/unit_test1.cpp:32: Failure
// Value of: std::is_sorted(items.begin(), items.end())
// Actual: false
// Expected: true
// Expected ordered, but got {1, 3, 5, 4, 2}
// [ FAILED ] vector_tests.items_is_ordered (0 ms)
// [----------] 5 tests from vector_tests (0 ms total)
//
// [----------] Global test environment tear-down
// [==========] 5 tests from 1 test suite ran. (0 ms total)
// [ PASSED ] 4 tests.
// [ FAILED ] 1 test, listed below:
// [ FAILED ] vector_tests.items_is_ordered
//
// 1 FAILED TEST

With Catch2​

#include <vector>
#include <algorithm>
#include <catch2/catch_all.hpp>

namespace collection_tests {
TEST_CASE("vector_tests") {
std::vector<int> items = {1, 3, 5, 4, 2};

SECTION("items_size_equals_5") {
REQUIRE(items.size() == 5);
}

SECTION("items_are_equivalent_to_1_2_3_4_5") {
std::vector<int> expected = {1, 2, 3, 4, 5};
auto sorted_expected = expected;
auto sorted_items = items;
std::sort(sorted_expected.begin(), sorted_expected.end());
std::sort(sorted_items.begin(), sorted_items.end());
REQUIRE(sorted_items == sorted_expected);
}

SECTION("items_contains_2_and_4") {
for (int v : {2, 4})
REQUIRE(std::find(items.begin(), items.end(), v) != items.end());
}

SECTION("items_does_not_contain_6") {
REQUIRE(std::find(items.begin(), items.end(), 6) == items.end());
}

SECTION("items_is_ordered") {
REQUIRE(std::is_sorted(items.begin(), items.end()));
}
}
}
// This code produces the following output :
//
// Randomness seeded to: 2162595183
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// unit_test_project2 is a Catch2 v3.11.0 host application.
// Run with -? for options
//
// -------------------------------------------------------------------------------
// vector_tests
// items_is_ordered
// -------------------------------------------------------------------------------
// /!---OMITTED---!/unit_test_project2/src/unit_test1.cpp:31
// ...............................................................................
//
// /!---OMITTED---!/unit_test_project2/src/unit_test1.cpp:32: FAILED:
// REQUIRE( std::is_sorted(items.begin(), items.end()) )
// with expansion:
// false
//
// ===============================================================================
// test cases: 1 | 1 failed
// assertions: 6 | 5 passed | 1 failed

With xtd.tunit​

#include <vector>
#include <xtd/xtd>

namespace collection_tests {
class test_class_(vector_tests) {
std::vector<int> items = {1, 3, 5, 4, 2};

void test_method_(items_size_equals_5) {
assert::are_equal(5, items.size());
}

void test_method_(items_are_equivalent_to_1_2_3_4_5) {
collection_assert::are_equivalent({1, 2, 3, 4, 5}, items);
}

void test_method_(items_conatains_2_and_4) {
collection_assert::contains({2, 4}, items);
}

void test_method_(items_does_not_conatain_6) {
collection_assert::does_not_contain({6}, items);
}

void test_method_(items_is_ordered) {
collection_assert::is_ordered(items);
}
};
}

// This code produces the following output :
//
// Start 5 tests from 1 test case
// Run tests:
// SUCCEED collection_tests::vector_tests.items_size_equals_5 [< 1 ms]
// SUCCEED collection_tests::vector_tests.items_are_equivalent_to_1_2_3_4_5 [< 1 ms]
// SUCCEED collection_tests::vector_tests.items_conatains_2_and_4 [< 1 ms]
// SUCCEED collection_tests::vector_tests.items_does_not_conatain_6 [< 1 ms]
// FAILED collection_tests::vector_tests.items_is_ordered [< 1 ms]
// Expected: <ordered>
// But was: < 1, 3, 5, 4, 2 >
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:25
//
// Test results:
// SUCCEED 4 tests.
// FAILED 1 test.
// End 5 tests from 1 test case ran. [0.0004 seconds]

Remarks​

To go further​

  • xtd.tunit also provides the possibility of ensuring that a test is in the right conditions to run:

Beyond simple assertions, xtd.tunit also provides advanced features such as assumptions and validations.

#include <xtd/xtd>

namespace system_tests {
class test_class_(fundamental_types) {
void test_method_(int_size) {
assume::is_true(environment::compiler_version().is_64_bit());
assert::are_equal(4, sizeof(int));
}
};
}

// This code produces the following output is build in 32 bits :
//
// Start 1 test from 1 test case
// Run tests:
// ABORTED system_tests::fundamental_types.int_size [< 1 ms]
// Test aborted
// Expected: true
// But was: false
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:6
//
// Test results:
// ABORTED 1 test.
// End 1 test from 1 test case ran. [0.0004 seconds]

Unlike assertions, validations do not stop the test when a check fails, allowing you to gather multiple failures in a single run.

#include <xtd/xtd>

namespace system_tests {
class test_class_(fundamental_types) {
void test_method_(int_operations) {
int i = 6;
valid::are_equal(14, i + 5);
valid::are_equal(4, i - 5);
valid::are_equal(3, i / 3);
valid::are_equal(27, i * 3);
}
void test_method_(short_operations) {
short i = 6;
assert::are_equal(14, i + 5);
assert::are_equal(4, i - 5);
assert::are_equal(3, i / 3);
assert::are_equal(27, i * 3);
}
};
}

// This code produces the following output :
//
// Start 2 tests from 1 test case
// Run tests:
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
// Expected: 14
// But was: 11
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:7
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
// Expected: 4
// But was: 1
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:8
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
// Expected: 3
// But was: 2
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:9
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
// Expected: 27
// But was: 18
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:10
// FAILED system_tests::fundamental_types.short_operations [< 1 ms]
// Expected: 14
// But was: 11
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:14
//
// Test results:
// FAILED 2 tests.
// End 2 tests from 1 test case ran. [0.0005 seconds]

See also​