![]() |
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 TestAsyncSetup and TeardownTestGroup and DescribeFor 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.cppfile.
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_NAMENote: lowercase versions of these
#defineare 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_NAMEare 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
#definemethod.
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_NAMEare 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
.cppfile, you must include it in your build project before your tests.For example, with
xmakeadd_filesorCMakein 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
.hheader 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/--timeoutoption
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
snowhousewith theSpecsadapter, see Installing SpecsReminder: your
main.cppneeds to include<Specs/Snowhouse/Setup.h>to enable thesnowhouseadapter.
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
libassertwith theSpecsadapter, see Installing SpecsReminder: the
assert()macro does not work inreleasemode. Recommended: useassert_thatinstead.
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:
SetupAsyncTeardownAsyncOneTimeSetupAsyncOneTimeTeardownAsyncThese 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/--timeoutoption
If you need to access information about the current setup, you can use the following variables:
current_setupcurrent_groupcurrent_testIf you need to access information about the current teardown, you can use the following variables:
current_teardowncurrent_groupcurrent_testFor 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 1Tests_Two is a defined as a group containing one test named Test 2This 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
TestGroupwhen 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_GROUPSPEC_GROUP, use #define SPEC_FILE instead of #define SPEC_GROUPTestGroup is a peer of every other TestGroup defined in the fileTestGroup cannot be nestedStartTestGroup/EndTestGroup or DescribeTestGroup("Group Name") can be used multiple times to define multiple top-level groupsRecommended usage
- Use
StartTestGroup/EndTestGroupwhen you need nested groups
(i.e. you need more than one level of groups)- You could also use this instead of
TestGroupif 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_GROUPSPEC_GROUP, use #define SPEC_FILE instead of #define SPEC_GROUPEndTestGroup() ends the current nested groupEndTestGroup()!Recommended usage
- Use
Describewhen 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_GROUPSPEC_GROUP, use #define SPEC_FILE instead of #define SPEC_GROUPtest/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
autolambda parameters, so useSpecDoneif desired.
If you need to access information about the current group, you can use the following variable:
current_groupFor more information, see đ Test Info.