mirror of
https://github.com/boostorg/test.git
synced 2026-01-29 08:02:12 +00:00
284 lines
11 KiB
XML
284 lines
11 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "../../../../tools/boostbook/dtd/boostbook.dtd" [
|
|
<!ENTITY utf "<acronym>UTF</acronym>">
|
|
]>
|
|
<section id="utf.user-guide.fixture">
|
|
<title>Fixtures … or let me repeat myself</title>
|
|
<titleabbrev>Fixtures</titleabbrev>
|
|
|
|
<para role="first-line-indented">
|
|
In general terms a test fixture or test context is the collection of one or more of the following items, required
|
|
to perform the test:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>preconditions</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>particular states of tested unites</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>necessary cleanup procedures</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para role="first-line-indented">
|
|
Though these tasks are encountered in many if not all test cases, what makes a test fixture different is
|
|
repetition. Where a normal test case implementation does all preparatory and cleanup work itself, a test fixture
|
|
allows this to be implemented in a separate reusable unit.
|
|
</para>
|
|
|
|
<para role="first-line-indented">
|
|
With introduction of extreme programming (XP), the testing style, that require test setup/cleanup repetition, is
|
|
becoming more and more popular. Single XP adopted test modules may contain hundreds of single assertion test cases,
|
|
many requiring very similar test setup/cleanup. This is the problem that the test fixture is designed to solve.
|
|
</para>
|
|
|
|
<para role="first-line-indented">
|
|
In practice a test fixture usually is a combination of setup and teardown functions, associated with test case.
|
|
The former serves the purposes of test setup; the later is dedicated to the cleanup tasks. Ideally it's
|
|
preferable that a test module author is able to define variables used in fixtures on the stack and the same time
|
|
is able to refer to them directly in test case.
|
|
</para>
|
|
|
|
<para role="first-line-indented">
|
|
It's important to understand that C++ provides a way to implement a straightforward test fixture solution
|
|
that almost satisfies our requirements without any extra support from the test framework. This may explain why
|
|
test fixtures support was introduced in the &utf; somewhat late in its life cycle. Here is how simple test module
|
|
with such a fixture may look like:
|
|
</para>
|
|
|
|
<btl-snippet name="snippet5"/>
|
|
|
|
<para role="first-line-indented">
|
|
This is a generic solution that can be used to implement any kind of shared setup or cleanup procedure. Still
|
|
there are several more or less minor practical issues with this pure C++ based fixtures solution:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
We need to add a fixture declaration statement into each test case manually.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Objects defined in fixture are references with "<fixture-instance-name>." prefix.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
There is no place to execute a "global" fixture, which performs "global" setup/cleanup
|
|
procedures before and after testing.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para role="first-line-indented">
|
|
While there is little the &utf; can do to address these issues for manually registered test units, it's
|
|
possible to resolve them for test units that are automatically registered. To do this the &utf; defines a
|
|
<link linkend="utf.user-guide.fixture.model">generic fixture model</link> - fixed interfaces that both setup and
|
|
teardown fixture functions should comply to. Based on the generic fixture model, the &utf; presents solution for
|
|
the following tasks:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><link linkend="utf.user-guide.fixture.per-test-case">per test case fixture automation</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link linkend="utf.user-guide.fixture.test-suite-shared">shared test suite level fixture</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link linkend="utf.user-guide.fixture.global">"global" fixture support</link></simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<section id="utf.user-guide.fixture.model">
|
|
<title>Generic fixture model</title>
|
|
<titleabbrev>Generic model</titleabbrev>
|
|
|
|
<para role="first-line-indented">
|
|
The &utf; defines the generic fixture model as follows:
|
|
</para>
|
|
|
|
<programlisting>struct <fixture-name>{
|
|
<fixture-name>(); // setup function
|
|
~<fixture-name>(); // teardown function
|
|
};</programlisting>
|
|
|
|
<para role="first-line-indented">
|
|
In other words a fixture is expected to be implemented as a class where the class constructor serves as a setup
|
|
method and class destructor serves as teardown method. The &utf; opted to avoid explicit names in fixture
|
|
interface for setup and teardown methods, since is considered most natural in C++ for tasks similar to RAII and
|
|
coincides with the pure C++ solution discusses earlier.
|
|
</para>
|
|
|
|
<important>
|
|
<simpara>
|
|
The above interface prevents you to report errors in the teardown procedure using an exception. It does make
|
|
sense though: if somehow more than one fixture is assigned to a test unit, you want all teardown procedures to
|
|
run, even if some may experience problems.
|
|
</simpara>
|
|
</important>
|
|
</section>
|
|
|
|
<section id="utf.user-guide.fixture.per-test-case">
|
|
<title>Per test case fixture</title>
|
|
<titleabbrev>Per test case</titleabbrev>
|
|
|
|
<para role="first-line-indented">
|
|
To automate the task of assigning a fixture for the test case, for test case creation use the macro
|
|
BOOST_FIXTURE_TEST_CASE in place of the macro <macroname>BOOST_AUTO_TEST_CASE</macroname>:
|
|
</para>
|
|
|
|
<inline-synopsis>
|
|
<macro name="BOOST_FIXTURE_TEST_CASE" kind="functionlike">
|
|
<macro-parameter name="test_case_name"/>
|
|
<macro-parameter name="fixture_name"/>
|
|
</macro>
|
|
</inline-synopsis>
|
|
|
|
<para role="first-line-indented">
|
|
The only difference from the macro <macroname>BOOST_AUTO_TEST_CASE</macroname> is the presence of an extra argument
|
|
- fixture name. Unlike the pure C++ solution you have direct access to the public and protected members of the
|
|
fixture, though you still need to refer to the fixture name in every test case.
|
|
</para>
|
|
|
|
<note>
|
|
<simpara>
|
|
You can't access private members of fixture, but then why would you make anything private?
|
|
</simpara>
|
|
</note>
|
|
|
|
<btl-example name="example18">
|
|
<title>Per test case fixture</title>
|
|
|
|
<para role="first-line-indented">
|
|
In this example only test_case1 and test_case2 have fixture F assigned. In the next section you going to see
|
|
what can be done if all test cases in a test suite require the same fixture.
|
|
</para>
|
|
</btl-example>
|
|
</section>
|
|
|
|
<section id="utf.user-guide.fixture.test-suite-shared">
|
|
<title>Test suite level fixture</title>
|
|
<titleabbrev>Test suite shared</titleabbrev>
|
|
|
|
<para role="first-line-indented">
|
|
If all test cases in a test require the same fixture (you can group test cases in the test suite based on a
|
|
fixture required) you can make another step toward an automation of a test fixture assignment. To assign the
|
|
same shared fixture for all test cases in a test suite use the macro BOOST_FIXTURE_TEST_SUITE in place of the
|
|
macro <macroname>BOOST_AUTO_TEST_SUITE</macroname> for automated test suite creation and registration.
|
|
</para>
|
|
|
|
<inline-synopsis>
|
|
<macro name="BOOST_FIXTURE_TEST_SUITE" kind="functionlike">
|
|
<macro-parameter name="suite_name"/>
|
|
<macro-parameter name="fixure_name"/>
|
|
</macro>
|
|
</inline-synopsis>
|
|
|
|
<para role="first-line-indented">
|
|
Once again the only difference from the macro <macroname>BOOST_AUTO_TEST_SUITE</macroname> usage is the presence of
|
|
an extra argument - the fixture name. And now, you not only have direct access to the public and protected members
|
|
of the fixture, but also do not need to refer to the fixture name in test case definition. All test cases assigned
|
|
the same fixture automatically.
|
|
</para>
|
|
|
|
<tip>
|
|
<simpara>
|
|
If necessary you can reset the fixture for a particular test case with the use of the macro
|
|
<macroname>BOOST_FIXTURE_TEST_CASE</macroname>.
|
|
</simpara>
|
|
</tip>
|
|
|
|
<note>
|
|
<simpara>
|
|
The fixture assignment is "deep". In other words unless reset by another
|
|
<macroname>BOOST_FIXTURE_TEST_SUITE</macroname> or <macroname>BOOST_FIXTURE_TEST_CASE</macroname> definition the
|
|
same fixture is assigned to all test cases, including ones that belong to the sub test suites.
|
|
</simpara>
|
|
</note>
|
|
|
|
<btl-example name="example19">
|
|
<title>Test suite level fixture</title>
|
|
</btl-example>
|
|
</section>
|
|
|
|
<section id="utf.user-guide.fixture.global">
|
|
<title>Global fixture</title>
|
|
|
|
<para role="first-line-indented">
|
|
Any global initialization that needs to be performed every time testing begins or a global cleanup that is to be
|
|
performed once testing is finished is called a global fixture. The &utf; global fixture design is based on a
|
|
generic test fixture model and is supported by the utility class boost::unit_test::global_fixture. The global
|
|
fixture design allows any number of global fixtures to be defined in any test file that constitutes a test module.
|
|
Though some initialization can be implemented in the test module initialization function, there are several
|
|
reasons to prefer the global fixture approach:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>There is no place for cleanup/teardown operations in the initialization function.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Unlike the initialization function, the global fixture setup method invocation is guarded by the execution
|
|
monitor. That means that all uncaught errors that occur during initialization are properly reported.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Any number of different global fixtures can be defined, which allows you to split initialization code by
|
|
category.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
The fixture allows you to place matching setup/teardown code in close vicinity in your test module code.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
If the whole test tree is constructed automatically the initialization function is empty and auto-generated by
|
|
the &utf;. To introduce the initialization function can be more work than the use of a global fixture facility,
|
|
while global fixture is more to the point.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Since all fixtures follow the same generic model you can easily switch from local per test case fixtures to
|
|
the global one.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
If you are using the interactive test runner (non-supplied yet) global test fixtures are applied to every run,
|
|
while an initialization function is executed only once during a test module startup (just make sure that
|
|
it's what you really want).
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para role="first-line-indented">
|
|
To define a global test module fixture you need to implement a class that matched generic fixture model and
|
|
passed it as an argument to the macro BOOST_GLOBAL_FIXTURE.
|
|
</para>
|
|
|
|
<inline-synopsis>
|
|
<macro name="BOOST_GLOBAL_FIXTURE" kind="functionlike">
|
|
<macro-parameter name="fixure_name"/>
|
|
</macro>
|
|
</inline-synopsis>
|
|
|
|
<para role="first-line-indented">
|
|
The statement, that performs global fixture definition, has to reside at a test file scope.
|
|
</para>
|
|
|
|
<btl-example name="example20">
|
|
<title>Global fixture</title>
|
|
</btl-example>
|
|
</section>
|
|
</section> |