UnitTests: How to write tests

Change-Id: Ic2221a83d139e7d7ad7b4d0f40d602822fb6654c
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2024-06-06 12:55:16 +02:00
parent 048df016ab
commit b9f28b957c

View File

@@ -11,10 +11,156 @@ All tests here depend on the [GoogleTest][1] framework.
## Best Practices ## Best Practices
We're following those patterns/approaches; We follow these patterns/approaches for structuring our tests:
* The Arrange, Act, and Assert (AAA) Pattern Arrange, Act, and Assert (AAA) / Given When Then (GWT)
* Given When Then (GWT) Pattern
This pattern structures your tests into three distinct sections:
Arrange (Given): Set up the initial conditions and inputs.
Act (When): Execute the code being tested.
Assert (Then): Verify the result.
The test name is descriptive and uses underlines for readability.
In the Act block, only the code you want to test should exist. Don't
add the setup code there. It makes the debugging harder because you have
to step over it.
```cpp
TEST(String, default_string_is_empty)
{
String text;
bool isEmpty = text.empty();
ASSERT_TRUE(isEmpty);
}
```
If the code block gets hard to read, you can write Arrange, Act, and Assert comments.
So the blocks are easier to identify. Don't write other comments. Use a descriptive
test name.
```cpp
TEST(String, default_string_is_empty)
{
// arrange
String text;
// act
bool isEmpty = text.empty();
// assert
ASSERT_TRUE(isEmpty);
}
```
If you use a fixture, you can sometimes skip the arrange part.
Fixtures should have the same name as your class. If you test functions,
it is the namespace. Sometimes you need multiple fixtures. Then you append
a descriptive text. You can put an underline in between.
```cpp
class String : public ::testing::Test
{
protected:
String text;
}
TEST_F(String, default_string_is_empty)
{
bool isEmpty = text.empty();
ASSERT_TRUE(isEmpty);
}
```
For mocks, you have to reverse the order of act and assert.
```cpp
class String : public ::testing::Test
{
protected:
NiceMock<OutputMock> outputMock;
Printer printer;
}
TEST_F(String, printer_appends_message_to_the_end)
{
// arrange
String text;
// assert
EXPECT_CALL(outputMock, print(EndsWith(text)))
// act
printer.print(text);
}
```
Don't write loops or branches in tests. Google Tests has many facilities supporting you to write clear tests.
Matcher, for example, gives a much better error message.
```cpp
TEST_F(Storage, return_all_entries_with_the_name_which_starts_with_foo)
{
storage.load(filepath);
auto entries = storage.entries("foo*");
ASSERT_THAT(entries, UnorderedElementsAre(IsEntry("foo", Field(&Entry::values), Contains(5)),
IsEntry("fooBar", Field(&Entry::values), IsSubset(42, 77))));
}
```
You can even make the matcher easier to read.
```cpp
template<typename Matcher>
auto FieldValues(const Matcher &matcher)
{
return Field(&Entry::values, matcher);
}
TEST_F(Storage, return_all_entries_with_the_name_which_starts_with_foo)
{
storage.load(filepath);
auto entries = storage.entries("foo*");
ASSERT_THAT(entries, UnorderedElementsAre(IsEntry("foo", FieldValues(Contains(5)),
IsEntry("fooBar", FieldValues(IsSubset(42, 77))));
}
```
## Avoid
Don't use using namespaces. It leads easily to name collisions.
```cpp
using namespace QmlDesigner;
```
If you have long namespace names, you can use:
```cpp
namespace ModelUtils = QmlDesigner::ModelUtils;
```
You can import single types too. But mind, that your tests are written
for reading, not for writing. They are part of the code documentation.
```cpp
using QmlDesigner::ModelNode;
using Node = QmlDesigner::ModelNode;
```
There are exceptions like literal namespaces.
```cpp
using namespace Qt::Literals;
```
## Test Organization ## Test Organization