2
0
mirror of https://github.com/boostorg/test.git synced 2026-01-29 08:02:12 +00:00
Files
test/doc/src/utf.user-guide.fixture.xml
Gennadiy Rozental dbec26921f latest state of sources
Fixes #4982

[SVN r75007]
2011-10-17 11:13:55 +00:00

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 &hellip; 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 &quot;&lt;fixture-instance-name&gt;.&quot; prefix.
</simpara>
</listitem>
<listitem>
<simpara>
There is no place to execute a &quot;global&quot; fixture, which performs &quot;global&quot; 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">&quot;global&quot; 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 &lt;fixture-name&gt;{
&lt;fixture-name&gt;(); // setup function
~&lt;fixture-name&gt;(); // 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 &quot;deep&quot;. 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>