![]() |
Specs
Beautiful C++ Test Framework
|
It's time to write your first tests!
This assumes that you have completed the steps outlines in Installing Specs
This page will cover:
Test
and TestAsync
Setup
and Teardown
TestGroup
and Describe
For more detailed information on the syntax of the test files, see Syntax Reference
To create tests, you will need to either:
Specs.exe
test runner can load multiple shared libraries of tests)Create a new file named main.cpp
.
Assuming you are creating a binary project, add the following code to main.cpp
:
This will define the main()
entrypoint for the executable.
If you are creating a shared library, add the following code to main.cpp
:
This will define the entrypoint function used by the Specs
test loader, e.g. when loading one or more shared libraries of tests from a Specs.exe
test runner.
To learn more about grouping specs into shared libraries, see Shared Libraries
đĄ Tip: If you are using
snowhouse
, you should#include <Specs/Snowhouse/Setup.h>
in your main binary or shared library.cpp
file.
Now you are ready to define a file with one or more tests.
Create a new file named MyFirstTests.cpp
.
Add the following code to MyFirstTests.cpp
:
This defines a new group of tests with the description "My First Tests"
.
Any tests defined in this file will be added to this group.
Every .cpp
file containing tests must have one of these unique #define
at the top of the file:
#define SPEC_CONTEXT UNIQUE_NAME
#define SPEC_FILE UNIQUE_NAME
#define SPEC_GROUP UNIQUE_NAME
#define SPEC_TEMPLATE UNIQUE_NAME
Note: lowercase versions of these
#define
are also supported:
#define spec_context UNIQUE_NAME
#define spec_file UNIQUE_NAME
#define spec_group UNIQUE_NAME
#define spec_template UNIQUE_NAME
Using #define spec_context UNIQUE_NAME
will ensure that the Test
, Setup
, Teardown
, etc
macros defined in this file do not conflict with macros defined in other files.
That's it, though. The content in your file might be added to a group of tests from another file, because spec_context
does not define a group nor does it clear the currently active group.
Use of SPEC_CONTEXT
is not recommended.
If you #define spec_file UNIQUE_NAME
, then the tests in the file are added to a global group.
The tests (and setup/teardown code) in this file will be run for all tests.
This could be useful for defining global setup/teardown code:
If you #define SPEC_GROUP UNIQUE_NAME
, then the tests in the file are added to a group with the description of UNIQUE_NAME
.
Note: underscores in
UNIQUE_NAME
are replaced with spaces in the description.
This is a good way to define a group of tests that are related to each other.
And, for example, in another file...
Specs
supports defining templates.
To learn more about templates, see Shared Code Templates
Note: there are a variety of other ways to define templates, this this is the
#define
method.
Templates are a way of defining Setup
and Teardown
and Test
code that can be shared and reused by multiple test groups.
If you #define SPEC_TEMPLATE UNIQUE_NAME
, then the tests in the file are added to a template with the description of UNIQUE_NAME
.
Note: underscores in
UNIQUE_NAME
are replaced with spaces in the description.
This is a good way to define a template that can be used by multiple test groups.
And then, in another test file...
â ī¸ WARNING: If you define a template like this in a
.cpp
file, you must include it in your build project before your tests.For example, with
xmake
add_files
orCMake
in youradd_executable
, be sure to include the template file before the test files.Otherwise, the templates will not exist at the time
UseTemplate()
is used.To see other ways of defining templates (e.g. in
.h
header files), see Shared Code Templates
Creating tests is easy!
Each test simply needs a description and a body.
That's it!
If you would like to skip a test, e.g. temporarily, you can mark it with Skip
.
If you need to access information about the current test, you can use the current_test
variable.
For more information, see đ Test Info.
Creating tests with long-running code is just as easy!
If an async test does not call done()
within a configurable timeout, the test will fail with a timeout error.
You can override the default timeout milliseconds using SetTimeout
.
âšī¸ Default timeout milliseconds can be configured via the
-t/--timeout
option
Tests aren't particularly useful without assertions!
Specs
provides built-in support for catching exceptions thrown during test execution.
Any exceptions thrown during test execution will be caught and reported as a failure.
The following exception types have special handling:
std::exception
(and derived types)what()
message is reported as the failure messageconst char*
)Any other exception types will be reported as a failure with a basic message like Unknown exception
.
It is recommended to use Specs
with an assertion library, such as snowhouse
or libassert
.
For instructions on installing
snowhouse
with theSpecs
adapter, see Installing SpecsReminder: your
main.cpp
needs to include<Specs/Snowhouse/Setup.h>
to enable thesnowhouse
adapter.
When the Specs
adapter for snowhouse
is included, you can use snowhouse
assertions in your tests.
Failed snowhouse
assertions will be reported as failures, including:
For instructions on installing
libassert
with theSpecs
adapter, see Installing SpecsReminder: the
assert()
macro does not work inrelease
mode. Recommended: useassert_that
instead.
When the Specs
adapter for libassert
is included, you can use libassert
assertions in your tests.
Failed libassert
assertions will be reported as failures, including:
Every test group supports the following:
Setup
: Runs before each test in the groupTeardown
: Runs after each test in the groupOneTimeSetup
: Runs once before the first test in the groupOneTimeTeardown
: Runs once after the last test in the groupTest groups can define as many of each of these as desired.
If you nest test groups, this is the order of operations:
OneTimeSetup
of the outermost group is runOneTimeSetup
of each nested group is runSetup
of the outermost group is runSetup
of each nested group is runTest
of each nested group is runTeardown
of each nested group is runTeardown
of the outermost group is runOneTimeTeardown
of each nested group is runIf any Setup
fails:
Setup
in that group are skippedTest
in that group is skippedTeardown
are still all executedSetup
is in a nested group, the Teardown
of the outermost group are still executedAll of the setup and teardown macros support Asynchronous code:
SetupAsync
TeardownAsync
OneTimeSetupAsync
OneTimeTeardownAsync
These work identically to TestAsync
, which must call done()
to mark the setup/teardown as finished.
Just like TestAsync
, if an async setup/teardown does not call done()
within a configurable timeout, the test will fail with a timeout error.
Just like TestAsync
, you can override the default timeout milliseconds using SetTimeout
.
âšī¸ Default timeout milliseconds can be configured via the
-t/--timeout
option
If you need to access information about the current setup, you can use the following variables:
current_setup
current_group
current_test
If you need to access information about the current teardown, you can use the following variables:
current_teardown
current_group
current_test
For more information, see đ Test Info.
Grouping your tests is a critical part of writing tests.
It is recommended to use #define SPEC_GROUP UNIQUE_NAME
to define a group for each .cpp
file containing tests.
TestsOne.cpp
TestsTwo.cpp
In the above code:
Tests_One
is a defined as a group containing one test named Test 1
Tests_Two
is a defined as a group containing one test named Test 2
This works great, but what if you want to group your tests into sub-groups?
Once you need to define sub-groups, there are a few different syntax styles to choose from:
TestGroup
defines a new top-level group (within the file group defined via spec_name)StartTestGroup
/EndTestGroup
define nested groupsDescribe
defines a nested group and child tests/groups/setups/teardowns are defined using a lambda syntaxRecommended usage
- Use
TestGroup
when you do not need nested groups
(i.e. you only need one level of groups)
TestGroup("Group Name")
defines a new top-level group#define SPEC_GROUP
is used, this group is a child of the group defined by SPEC_GROUP
SPEC_GROUP
, use #define SPEC_FILE
instead of #define SPEC_GROUP
TestGroup
is a peer of every other TestGroup
defined in the fileTestGroup
cannot be nestedStartTestGroup
/EndTestGroup
or Describe
TestGroup("Group Name")
can be used multiple times to define multiple top-level groupsRecommended usage
- Use
StartTestGroup
/EndTestGroup
when you need nested groups
(i.e. you need more than one level of groups)- You could also use this instead of
TestGroup
if you prefer the nested{ ... }
syntax
StartTestGroup("Group Name")
defines a new nested group#define SPEC_GROUP
is used, this group is a child of the group defined by SPEC_GROUP
SPEC_GROUP
, use #define SPEC_FILE
instead of #define SPEC_GROUP
EndTestGroup()
ends the current nested groupEndTestGroup()
!Recommended usage
- Use
Describe
when you need nested groups and prefer to use the lambda syntax
Describe("Group Name")
defines a new nested group#define SPEC_GROUP
is used, this group is a child of the group defined by SPEC_GROUP
SPEC_GROUP
, use #define SPEC_FILE
instead of #define SPEC_GROUP
test
/setup
/teardown
can be used inside of a Describe
blockThe lambda syntax is used to define tests/groups/setups/teardowns inside of a Describe
block.
To create an async test/setup/teardown, your lambda should accept a done
parameter.
You can use auto done
or you can use SpecDone done
*
Some IDEs do not support intellisense of
auto
lambda parameters, so useSpecDone
if desired.
If you need to access information about the current group, you can use the following variable:
current_group
For more information, see đ Test Info.