forked from qt-creator/qt-creator
UnitTests: How to write tests
Change-Id: Ic2221a83d139e7d7ad7b4d0f40d602822fb6654c Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -11,10 +11,156 @@ All tests here depend on the [GoogleTest][1] framework.
|
||||
|
||||
## Best Practices
|
||||
|
||||
We're following those patterns/approaches;
|
||||
We follow these patterns/approaches for structuring our tests:
|
||||
|
||||
* The Arrange, Act, and Assert (AAA) Pattern
|
||||
* Given When Then (GWT) Pattern
|
||||
Arrange, Act, and Assert (AAA) / Given When Then (GWT)
|
||||
|
||||
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
|
||||
|
||||
|
Reference in New Issue
Block a user