Skip to content

Commit 8e22659

Browse files
committed
Add tips & Tricks : Unit testing with xtd.tunit
1 parent 78d0519 commit 8e22659

File tree

2 files changed

+301
-1
lines changed

2 files changed

+301
-1
lines changed

docs/documentation/internal/tips_and_triks_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
| 🔴 | #13 | December 22, 2025 | Drawing text and shapes in a form with xtd::drawing::graphics | Qt painting / GDI+ |
3838
| 🔴 | #12 | December 8, 2025 | Using timers (interval, elapsed event) | std::thread + sleep, Qt Timer |
3939
| 🔴 | #11 | November 24, 2025 | Formatting strings with xtd::string::format | C++20 std::format / Qt QString::arg |
40-
| 🔴 | #10 | November 10, 2025 | Unit testing with xtd::tunit | Catch2 / gtest |
40+
| 🟢 | #10 | November 10, 2025 | Unit testing with xtd::tunit | Catch2 / gtest |
4141
| 🟢 | #9 | October 27, 2025 | Adding color to console output (text + background) | ANSI escape codes |
4242
| 🟢 | #8 | October 13, 2025 | array_list a heterogeneous container supporting multiple types | C++ modern / Boost / Qt |
4343
| 🟢 | #7 | September 29, 2025 | Creating a simple form with a button | Qt / WinForms |
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# Unit testing with xtd.tunit (🟡 Intermediate)
2+
3+
How to create a simple unit test application using xtd.tunit, and how it compares to Google Test and Catch2.
4+
5+
We will validate several properties of a simple integer collection: `std::vector<int> items = {1, 3, 5, 4, 2};`
6+
Validations performed :
7+
* Check that the collection size equals 5.
8+
* Verify that it contains the elements 1, 2, 3, 4, and 5 (regardless of order).
9+
* Verify that it contains the items 2 and 4.
10+
* Verify that it does not contain 6.
11+
* Verify that the collection is ordered.
12+
13+
## With Google Tests
14+
15+
```cpp
16+
#include <vector>
17+
#include <algorithm>
18+
#include <gtest/gtest.h>
19+
20+
namespace collection_tests {
21+
class vector_tests : public ::testing::Test {
22+
protected:
23+
std::vector<int> items = {1, 3, 5, 4, 2};
24+
};
25+
26+
TEST_F(vector_tests, items_size_equals_5) {
27+
EXPECT_EQ(items.size(), 5);
28+
}
29+
30+
TEST_F(vector_tests, items_are_equivalent_to_1_2_3_4_5) {
31+
std::vector<int> expected = {1, 2, 3, 4, 5};
32+
auto sorted_items = items;
33+
std::sort(sorted_items.begin(), sorted_items.end());
34+
EXPECT_EQ(sorted_items, expected) << "Expected equivalent collections {1, 2, 3, 4, 5}, but got {1, 3, 5, 4, 2}";
35+
}
36+
37+
TEST_F(vector_tests, items_contains_2_and_4) {
38+
for (int v : {2, 4})
39+
EXPECT_NE(std::find(items.begin(), items.end(), v), items.end()) << "Expected to find " << v << " in collection.";
40+
}
41+
42+
TEST_F(vector_tests, items_does_not_contain_6) {
43+
EXPECT_EQ(std::find(items.begin(), items.end(), 6), items.end()) << "Expected collection not to contain 6.";
44+
}
45+
46+
TEST_F(vector_tests, items_is_ordered) {
47+
EXPECT_TRUE(std::is_sorted(items.begin(), items.end())) << "Expected ordered, but got {1, 3, 5, 4, 2}";
48+
}
49+
}
50+
51+
// This code produces the following output :
52+
//
53+
// [==========] Running 5 tests from 1 test suite.
54+
// [----------] Global test environment set-up.
55+
// [----------] 5 tests from vector_tests
56+
// [ RUN ] vector_tests.items_size_equals_5
57+
// [ OK ] vector_tests.items_size_equals_5 (0 ms)
58+
// [ RUN ] vector_tests.items_are_equivalent_to_1_2_3_4_5
59+
// [ OK ] vector_tests.items_are_equivalent_to_1_2_3_4_5 (0 ms)
60+
// [ RUN ] vector_tests.items_contains_2_and_4
61+
// [ OK ] vector_tests.items_contains_2_and_4 (0 ms)
62+
// [ RUN ] vector_tests.items_does_not_contain_6
63+
// [ OK ] vector_tests.items_does_not_contain_6 (0 ms)
64+
// [ RUN ] vector_tests.items_is_ordered
65+
// /!---OMITTED---!/unit_test_project1/src/unit_test1.cpp:32: Failure
66+
// Value of: std::is_sorted(items.begin(), items.end())
67+
// Actual: false
68+
// Expected: true
69+
// Expected ordered, but got {1, 3, 5, 4, 2}
70+
// [ FAILED ] vector_tests.items_is_ordered (0 ms)
71+
// [----------] 5 tests from vector_tests (0 ms total)
72+
//
73+
// [----------] Global test environment tear-down
74+
// [==========] 5 tests from 1 test suite ran. (0 ms total)
75+
// [ PASSED ] 4 tests.
76+
// [ FAILED ] 1 test, listed below:
77+
// [ FAILED ] vector_tests.items_is_ordered
78+
//
79+
// 1 FAILED TEST
80+
```
81+
82+
## With Catch2
83+
84+
```cpp
85+
#include <vector>
86+
#include <algorithm>
87+
#include <catch2/catch_all.hpp>
88+
89+
namespace collection_tests {
90+
TEST_CASE("vector_tests") {
91+
std::vector<int> items = {1, 3, 5, 4, 2};
92+
93+
SECTION("items_size_equals_5") {
94+
REQUIRE(items.size() == 5);
95+
}
96+
97+
SECTION("items_are_equivalent_to_1_2_3_4_5") {
98+
std::vector<int> expected = {1, 2, 3, 4, 5};
99+
auto sorted_expected = expected;
100+
auto sorted_items = items;
101+
std::sort(sorted_expected.begin(), sorted_expected.end());
102+
std::sort(sorted_items.begin(), sorted_items.end());
103+
REQUIRE(sorted_items == sorted_expected);
104+
}
105+
106+
SECTION("items_contains_2_and_4") {
107+
for (int v : {2, 4})
108+
REQUIRE(std::find(items.begin(), items.end(), v) != items.end());
109+
}
110+
111+
SECTION("items_does_not_contain_6") {
112+
REQUIRE(std::find(items.begin(), items.end(), 6) == items.end());
113+
}
114+
115+
SECTION("items_is_ordered") {
116+
REQUIRE(std::is_sorted(items.begin(), items.end()));
117+
}
118+
}
119+
}
120+
// This code produces the following output :
121+
//
122+
// Randomness seeded to: 2162595183
123+
//
124+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125+
// unit_test_project2 is a Catch2 v3.11.0 host application.
126+
// Run with -? for options
127+
//
128+
// -------------------------------------------------------------------------------
129+
// vector_tests
130+
// items_is_ordered
131+
// -------------------------------------------------------------------------------
132+
// /!---OMITTED---!/unit_test_project2/src/unit_test1.cpp:31
133+
// ...............................................................................
134+
//
135+
// /!---OMITTED---!/unit_test_project2/src/unit_test1.cpp:32: FAILED:
136+
// REQUIRE( std::is_sorted(items.begin(), items.end()) )
137+
// with expansion:
138+
// false
139+
//
140+
// ===============================================================================
141+
// test cases: 1 | 1 failed
142+
// assertions: 6 | 5 passed | 1 failed
143+
```
144+
145+
## With xtd.tunit
146+
147+
```cpp
148+
#include <vector>
149+
#include <xtd/xtd>
150+
151+
namespace collection_tests {
152+
class test_class_(vector_tests) {
153+
std::vector<int> items = {1, 3, 5, 4, 2};
154+
155+
void test_method_(items_size_equals_5) {
156+
assert::are_equal(5, items.size());
157+
}
158+
159+
void test_method_(items_are_equivalent_to_1_2_3_4_5) {
160+
collection_assert::are_equivalent({1, 2, 3, 4, 5}, items);
161+
}
162+
163+
void test_method_(items_conatains_2_and_4) {
164+
collection_assert::contains({2, 4}, items);
165+
}
166+
167+
void test_method_(items_does_not_conatain_6) {
168+
collection_assert::does_not_contain({6}, items);
169+
}
170+
171+
void test_method_(items_is_ordered) {
172+
collection_assert::is_ordered(items);
173+
}
174+
};
175+
}
176+
177+
// This code produces the following output :
178+
//
179+
// Start 5 tests from 1 test case
180+
// Run tests:
181+
// SUCCEED collection_tests::vector_tests.items_size_equals_5 [< 1 ms]
182+
// SUCCEED collection_tests::vector_tests.items_are_equivalent_to_1_2_3_4_5 [< 1 ms]
183+
// SUCCEED collection_tests::vector_tests.items_conatains_2_and_4 [< 1 ms]
184+
// SUCCEED collection_tests::vector_tests.items_does_not_conatain_6 [< 1 ms]
185+
// FAILED collection_tests::vector_tests.items_is_ordered [< 1 ms]
186+
// Expected: <ordered>
187+
// But was: < 1, 3, 5, 4, 2 >
188+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:25
189+
//
190+
// Test results:
191+
// SUCCEED 4 tests.
192+
// FAILED 1 test.
193+
// End 5 tests from 1 test case ran. [0.0004 seconds]
194+
```
195+
196+
## Remarks
197+
198+
* [xtd.tunit](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html) contains collection checks out of the box. It is therefore not necessary to write 'boilerplate' code to carry out the tests.
199+
* [xtd.tunit](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html) provides explicit and human-readable failure messages. The optional user message is meant to clarify the intent of the test, not to compensate for missing diagnostics.
200+
* [xtd.tunit](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html) has different classes to help you write your unit tests quickly:
201+
* [xtd::tunit::assert](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1assert.html)
202+
* [xtd::tunit::collection_assert](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1collection__assert.html)
203+
* [xtd::tunit::directory_assert](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1directory__assert.html)
204+
* [xtd::tunit::file_assert](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1file__assert.html)
205+
* [xtd::tunit::string_assert](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1string__assert.html)
206+
207+
## To go further
208+
* [xtd.tunit](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html) also provides the possibility of ensuring that a test is in the right conditions to run:
209+
210+
Beyond simple assertions, xtd.tunit also provides advanced features such as assumptions and validations.
211+
212+
```cpp
213+
#include <xtd/xtd>
214+
215+
namespace system_tests {
216+
class test_class_(fundamental_types) {
217+
void test_method_(int_size) {
218+
assume::is_true(environment::compiler_version().is_64_bit());
219+
assert::are_equal(4, sizeof(int));
220+
}
221+
};
222+
}
223+
224+
// This code produces the following output is build in 32 bits :
225+
//
226+
// Start 1 test from 1 test case
227+
// Run tests:
228+
// ABORTED system_tests::fundamental_types.int_size [< 1 ms]
229+
// Test aborted
230+
// Expected: true
231+
// But was: false
232+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:6
233+
//
234+
// Test results:
235+
// ABORTED 1 test.
236+
// End 1 test from 1 test case ran. [0.0004 seconds]
237+
```
238+
239+
* The classes [xtd::tunit::valid](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1valid.html), [xtd::tunit::collection_valid](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1collection__valid.html), [xtd::tunit::directory_valid](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1directory__valid.html), [xtd::tunit::file_valid](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1file__valid.html) and , [xtd::tunit::string_valid](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1tunit_1_1string__valid.html) contrary to asserts allow you to run a check but without stopping the test at the first error.
240+
241+
Unlike assertions, validations do not stop the test when a check fails, allowing you to gather multiple failures in a single run.
242+
243+
```cpp
244+
#include <xtd/xtd>
245+
246+
namespace system_tests {
247+
class test_class_(fundamental_types) {
248+
void test_method_(int_operations) {
249+
int i = 6;
250+
valid::are_equal(14, i + 5);
251+
valid::are_equal(4, i - 5);
252+
valid::are_equal(3, i / 3);
253+
valid::are_equal(27, i * 3);
254+
}
255+
void test_method_(short_operations) {
256+
short i = 6;
257+
assert::are_equal(14, i + 5);
258+
assert::are_equal(4, i - 5);
259+
assert::are_equal(3, i / 3);
260+
assert::are_equal(27, i * 3);
261+
}
262+
};
263+
}
264+
265+
// This code produces the following output :
266+
//
267+
// Start 2 tests from 1 test case
268+
// Run tests:
269+
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
270+
// Expected: 14
271+
// But was: 11
272+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:7
273+
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
274+
// Expected: 4
275+
// But was: 1
276+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:8
277+
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
278+
// Expected: 3
279+
// But was: 2
280+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:9
281+
// FAILED system_tests::fundamental_types.int_operations [< 1 ms]
282+
// Expected: 27
283+
// But was: 18
284+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:10
285+
// FAILED system_tests::fundamental_types.short_operations [< 1 ms]
286+
// Expected: 14
287+
// But was: 11
288+
// Stack Trace: in /!---OMITTED---!/unit_test_project3/src/unit_test1.cpp:14
289+
//
290+
// Test results:
291+
// FAILED 2 tests.
292+
// End 2 tests from 1 test case ran. [0.0005 seconds]
293+
```
294+
295+
## See also
296+
297+
* [xtd.tunit reference](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html)
298+
* [Tips & Tricks](/docs/documentation/tips_and_tricks)
299+
* [Tips & Tricks](/docs/documentation/tips_and_tricks)
300+
* [Documentation](/docs/documentation)

0 commit comments

Comments
 (0)