Test Unit: Difference between revisions
No edit summary |
No edit summary |
||
(13 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
__NOTOC__ | __NOTOC__ | ||
'''Stride''' groups similar test cases into a single ''runnable'' package called a '''Test Unit''' (also known as a '''Test Suite'''). These tests are written in C and C++ which are compiled and linked with your software and run, when called, on your target platform. They are suitable for both developer unit testing as well as end-to-end integration testing. An external [[Stride Runner]] is provided which controls the execution of '''Test Units''' and publishes test results to the local file system and optionally to [http://www.testspace.com Testspace]. The following is an example of the structure of a '''Test Unit''': | |||
'''Declare tests''' | '''Declare tests''' | ||
Line 7: | Line 6: | ||
#include <srtest.h> | #include <srtest.h> | ||
class MyTest { | class MyTest : public stride::srTest { | ||
public: | public: | ||
void CheckFoo(); | void CheckFoo(); | ||
Line 43: | Line 42: | ||
'''Test fixturing''' refers to the ''Setup'' and ''Teardown'' phases of the testing. | '''Test fixturing''' refers to the ''Setup'' and ''Teardown'' phases of the testing. | ||
In the '''Setup''' phase, we put all of the things into place that are required in order | In the '''Setup''' phase, we put all of the things into place that are required in order to run a test and expect a particular outcome. This includes things like: | ||
* Acquiring resources such as memory, hardware, etc. | * Acquiring resources such as memory, hardware, etc. | ||
* Setting up required states such as input files in place, memory filled with a pattern, dependencies initialized, etc. | * Setting up required states such as input files in place, memory filled with a pattern, dependencies initialized, etc. | ||
Line 55: | Line 54: | ||
Grouping of individual tests into a Test Unit can be accomplished in any of three ways: | Grouping of individual tests into a Test Unit can be accomplished in any of three ways: | ||
* A Test Unit can be comprised of the | * A Test Unit can be comprised of the public member functions of a '''C++ class''', | ||
* A Test Unit can be comprised of a set of '''C functions''', | * A Test Unit can be comprised of a set of '''C functions''', | ||
* A Test Unit can be comprised of C functions pointed to by members of a '''C struct''' | * A Test Unit can be comprised of C functions pointed to by members of a '''C struct''' | ||
Line 68: | Line 67: | ||
'''C++ class''' | '''C++ class''' | ||
[[ | [[Test_Unit#C++ Class|Example code]] | ||
Line 84: | Line 83: | ||
'''C class''' | '''C class''' | ||
[[ | [[Test_Unit#C Class|Example code]] | ||
| | | | ||
Line 92: | Line 91: | ||
| | | | ||
* Requires initialization code for structure function pointer setup | * Requires initialization code for structure function pointer setup | ||
* [[ | * [[Test_Documentation_in_C/C%2B%2B|Test Unit documentation]] must be associated with the structure function pointer members instead of the actual test method implementations. | ||
| <tt>scl_test_cclass</tt> | | <tt>scl_test_cclass</tt> | ||
|- | |- | ||
Line 98: | Line 97: | ||
'''C functions''' | '''C functions''' | ||
[[ | [[Test_Unit#Free Functions|Example code]] | ||
| | | | ||
Line 106: | Line 105: | ||
* No test unit support for constructor/initializer or destructor/de-initializer | * No test unit support for constructor/initializer or destructor/de-initializer | ||
* Changing test unit membership requires editing of the pragma statement | * Changing test unit membership requires editing of the pragma statement | ||
* [[ | * [[Test_Documentation_in_C/C%2B%2B|Test Unit documentation]] for the FList must be associated with the header file - as such, you can only have one FList per header file if you want to document your test units. | ||
| <tt>scl_test_flist</tt> | | <tt>scl_test_flist</tt> | ||
|} | |} | ||
Line 113: | Line 112: | ||
You can freely mix different deployment methods across a project if desired, the format of the results is consistent across all test unit packaging options. | You can freely mix different deployment methods across a project if desired, the format of the results is consistent across all test unit packaging options. | ||
== Test Method Return Values == | == Test Method Return Values == | ||
Test Method return values must conform to certain return types as | Test Method return values must conform to certain return types as summarized in the following table. | ||
Line 129: | Line 127: | ||
| Most common return type | | Most common return type | ||
| Since there is no return, PASS/FAIL status is set in the body of the method using a | | Since there is no return, PASS/FAIL status is set in the body of the method using a | ||
* [[Test_Macros| | * [[Test_Macros|Test Macro]] | ||
or a runtime call | or a runtime call | ||
* [[Runtime_Test_Services#srTestCaseSetStatus|srTestCaseSetStatus()]] | * [[Runtime_Test_Services#srTestCaseSetStatus|srTestCaseSetStatus()]] | ||
Line 168: | Line 166: | ||
== Examples == | == Examples == | ||
Following are a few short examples. In each example, a single test unit with the name "MyTest" is identified to the | Following are a few short examples. In each example, a single test unit with the name "MyTest" is identified to the Stride compiler via [[Test Pragmas]]. | ||
=== C++ Class === | === C++ Class === | ||
Line 192: | Line 190: | ||
#ifdef _SCL | #ifdef _SCL | ||
// this pragma identifies MyTest as a test class to the | // this pragma identifies MyTest as a test class to the Stride compiler | ||
#pragma scl_test_class(MyTest) | #pragma scl_test_class(MyTest) | ||
#endif | #endif | ||
Line 214: | Line 212: | ||
#ifdef _SCL | #ifdef _SCL | ||
// This pragma identifies MyTest as a test c class to the | // This pragma identifies MyTest as a test c class to the Stride compiler. | ||
// Extra instrumentation code will be generated to call MyTest_Init() before | // Extra instrumentation code will be generated to call MyTest_Init() before | ||
// tests are run. | // tests are run. | ||
Line 253: | Line 251: | ||
#ifdef _SCL | #ifdef _SCL | ||
// this pragma identifies MyTest as a test unit to the | // this pragma identifies MyTest as a test unit to the Stride compiler, specifying | ||
// the four functions as members of the test unit | // the four functions as members of the test unit | ||
#pragma scl_test_flist("MyTest", ExpectPass, ExpectFail, ChangeMyName, ChangeMyDescription) | #pragma scl_test_flist("MyTest", ExpectPass, ExpectFail, ChangeMyName, ChangeMyDescription) | ||
Line 280: | Line 278: | ||
=== Use Operator << to Augment Test Macros === | === Use Operator << to Augment Test Macros === | ||
In C++ test code [[Test Point]], [[Test Log]] and [[Test | In C++ test code [[Test Point]], [[Test Log]] and [[Test Macros]] macros support adding to the annotation using the << operator. For example: | ||
<source lang="cpp"> | <source lang="cpp"> | ||
Line 289: | Line 287: | ||
* all numeric types, | * all numeric types, | ||
* C string (char* or wchar_t*), and | * C string (char* or wchar_t*), and | ||
* types allowing implicit cast to numeric type or "C" string. | * types allowing implicit cast to numeric type or "C" string. | ||
=== Overloading the << operator === | === Overloading the << operator === | ||
Line 324: | Line 322: | ||
} | } | ||
</source> | </source> | ||
Latest revision as of 19:58, 7 July 2015
Stride groups similar test cases into a single runnable package called a Test Unit (also known as a Test Suite). These tests are written in C and C++ which are compiled and linked with your software and run, when called, on your target platform. They are suitable for both developer unit testing as well as end-to-end integration testing. An external Stride Runner is provided which controls the execution of Test Units and publishes test results to the local file system and optionally to Testspace. The following is an example of the structure of a Test Unit:
Declare tests
#include <srtest.h>
class MyTest : public stride::srTest {
public:
void CheckFoo();
void CheckBoo();
};
#pragma scl_test_class(MyTest)
Write tests
#include <mytest.h>
void MyTest::CheckFoo() {
..
srEXPECT_EQ(foo(2 + 2), 4);
}
void MyTest::CheckBoo() {
..
srEXPECT_GT(boo(3 * 3), 7);
}
Test Unit
A single Test Unit is a set of Test Methods that always run together as an executable unit. Test Methods are the test cases that comprise a Test Unit. Each Test Method by default maps to a single Test Case in the results. When relying on this default behavior of Test Methods, we refer to these methods as static Test Cases. A less common technique is to create and add a Test Case dynamically at runtime to an existing Suite via the Test Services. We refer to these as dynamic Test Cases.
Individual test cases are implemented as test methods or functions which follow a four-phase testing pattern:
- Setting up a test fixture (optional)
- Exercising the Software Under Test (SUT)
- Verifying that the expected outcome has occurred
- Tearing down the test fixture (optional)
Test fixturing refers to the Setup and Teardown phases of the testing.
In the Setup phase, we put all of the things into place that are required in order to run a test and expect a particular outcome. This includes things like:
- Acquiring resources such as memory, hardware, etc.
- Setting up required states such as input files in place, memory filled with a pattern, dependencies initialized, etc.
In the Tear down phase, we clean up the fixturing we did in the Setup phase, leaving the system in a state that is ready to be used by the next test.
Packaging
Individual functions or methods, which typically implement a single test case are grouped into one or more Test Units which are executed as atomic entities.
Grouping of individual tests into a Test Unit can be accomplished in any of three ways:
- A Test Unit can be comprised of the public member functions of a C++ class,
- A Test Unit can be comprised of a set of C functions,
- A Test Unit can be comprised of C functions pointed to by members of a C struct
The following table compares the three packaging variants.
Test Unit Type | Advantages | Disadvantages | scl pragma |
---|---|---|---|
C++ class
|
|
|
scl_test_class |
C class |
|
|
scl_test_cclass |
C functions |
|
|
scl_test_flist |
The best choice is usually the C++ class since it offers the best mix of features and ease-of-use. (You can test code written in C or C++ using the C++ class test units.) However, compiling C++ is not always possible, in this case one of the C-based test unit packaging options must be used.
You can freely mix different deployment methods across a project if desired, the format of the results is consistent across all test unit packaging options.
Test Method Return Values
Test Method return values must conform to certain return types as summarized in the following table.
Return Type | Description | How Return Value is Interpreted | Default Status |
void | Most common return type | Since there is no return, PASS/FAIL status is set in the body of the method using a
or a runtime call |
|
integer type | Integer types include:
may include signed/unsigned/const qualifiers |
|
|
bool | c++ only |
|
|
Examples
Following are a few short examples. In each example, a single test unit with the name "MyTest" is identified to the Stride compiler via Test Pragmas.
C++ Class
MyTest.h
#include <srtest.h>
class MyTest : public stride::srTest
{
public:
void ExpectPass()
{
srNOTE_INFO("this test should pass");
srEXPECT_EQ(2 + 2, 4);
}
void ExpectFail()
{
srNOTE_INFO("this test should fail");
srEXPECT_GT(2 * 3, 7);
}
};
#ifdef _SCL
// this pragma identifies MyTest as a test class to the Stride compiler
#pragma scl_test_class(MyTest)
#endif
C Class
MyTest.h
#include <srtest.h>
typedef struct MyTest
{
void (*ExpectPass)(struct MyTest* self);
void (*ExpectFail)(struct MyTest* self);
} MyTest;
void MyTest_Init(MyTest* self);
#ifdef _SCL
// This pragma identifies MyTest as a test c class to the Stride compiler.
// Extra instrumentation code will be generated to call MyTest_Init() before
// tests are run.
#pragma scl_test_cclass(MyTest, MyTest_Init)
#endif
MyTest.c
#include "MyTest.h"
static void ExpectPass(MyTest* self)
{
srNOTE_INFO("this test should pass");
srEXPECT_EQ(2 + 2, 4);
}
static void ExpectFail(MyTest* self)
{
srNOTE_INFO("this test should fail");
srEXPECT_GT(2 * 3, 7);
}
void MyTest_Init(MyTest* self)
{
self->ExpectPass = ExpectPass;
self->ExpectFail = ExpectFail;
}
Free Functions
MyTest.h
#include <srtest.h>
void ExpectPass();
void ExpectFail();
#ifdef _SCL
// this pragma identifies MyTest as a test unit to the Stride compiler, specifying
// the four functions as members of the test unit
#pragma scl_test_flist("MyTest", ExpectPass, ExpectFail, ChangeMyName, ChangeMyDescription)
#endif
MyTest.c
#include "MyTest.h"
void ExpectPass()
{
srNOTE_INFO("this test should pass");
srEXPECT_EQ(2 + 2, 4);
}
void ExpectFail()
{
srNOTE_INFO("this test should fail");
srEXPECT_GT(2 * 3, 7);
}
C++ Only Features
Use Operator << to Augment Test Macros
In C++ test code Test Point, Test Log and Test Macros macros support adding to the annotation using the << operator. For example:
srEXPECT_TRUE(a != b) << "My custom message" << " with more data " << 1234;
As delivered, the macros will support stream input annotations for:
- all numeric types,
- C string (char* or wchar_t*), and
- types allowing implicit cast to numeric type or "C" string.
Overloading the << operator
You can also overload the << operator in order to annotate reports using your own custom type. An example is below.
The following will compile and execute successfully given that the << operator is overloaded as shown:
#include <srtest.h>
// MyCustomClass implementation
class MyCustomClass
{
public:
MyCustomClass(int i) : m_int(i) {}
private:
int m_int;
friend stride::Message& operator<<(stride::Message& ss, const MyCustomClass& obj);
};
stride::Message& operator<<(stride::Message& ss, const MyCustomClass& obj)
{
ss << obj.m_int;
return ss;
}
void test()
{
MyCustomClass custom(34);
srEXPECT_FALSE(true) << custom;
}