2
0
mirror of https://github.com/boostorg/test.git synced 2026-01-26 19:12:10 +00:00
Files
test/doc/tutorials/new_year_resolution.html
Gennadiy Rozental 860f1115c4 in progress
[SVN r33128]
2006-02-26 21:06:49 +00:00

192 lines
13 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>Boost.Test driven development</TITLE>
<LINK rel="stylesheet" type="text/css" href="../style/btl.css" media="screen">
<LINK rel="stylesheet" type="text/css" href="../style/btl-print.css" media="print">
<META http-equiv="Content-Language" content="en-us">
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</HEAD>
<BODY>
<DIV class="header"> <A href="../index.html">Boost.Test</A> &gt; <A href="../usage/recomendations.html">Tutorials
and usage recommendations</A> &gt; <SPAN class="current_article"> Boost.Test
driven development</SPAN></DIV>
<DIV class="body"> <IMG src="../btl1.gif" width="252" height="43" alt="Boost Test logo">
<H1>Boost.Test driven development or my New Year resolution</H1>
<P class="first-line-indented">Today is a momentous day - first day of new
year. Today I am going to start a new life.
I am going to stop eating a greasy food, start attending a fitness club and
... today I am going to test programs I am writing. I can start right after
the last line of a program is completed or, even better, I can write
tests while I am coding. And maybe next time I will write tests before the
coding, during the design stage. I have read a lot of literature on how to
write the tests, I have the unit test framework in hand and an idea of new
class. So let's get started.</P>
<P class="first-line-indented">Let say I want to encapsulate an unchangeable C character
buffer with a length into the simple class <B>const_string</B>. Rationale: a string
class that does not allocate a memory and provide a convenient read-only access
to the preallocated character buffer. I will probably want <B>const_string</B>
to have an interface similar to the class std::string. What will I do first? In
my new life I will start with writing a test module for future class <B>const_string</B>.
It will look like this:</P>
<P>const_string_test.cpp:</P>
<PRE class="code">#<SPAN class="reserv-word">define</SPAN> BOOST_TEST_MODULE const_string test
#<SPAN class="reserv-word">include</SPAN> &lt;boost/test/unit_test.hpp&gt;
<SPAN class="comment">// EOF</SPAN></PRE>
<P class="first-line-indented">Now I can compile it and link with the unit test
framework. Done! I have a working test program. It is empty, so when I run the
program it produces following output:</P>
<P class="test-output">*** No errors detected</P>
<P class="first-line-indented">Well, now it could be a good time to start a work
on <B>const_string</B>. First thing I imagine would be good to have is a constructors
and trivial access methods. So my class initial version looks like this:</P>
<P>const_string.hpp</P>
<PRE class="code"><SPAN class="reserv-word">class</SPAN> const_string {
<SPAN class="reserv-word">public</SPAN>:
// Constructors
const_string();
const_string( <SPAN class="cpp-type">std::string</SPAN> <SPAN class="reserv-word">const</SPAN>&amp; s )
const_string( <SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">const</SPAN>* s );
const_string( <SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">const</SPAN>* s, <SPAN class="cpp-type">size_t</SPAN> length );
const_string( <SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">const</SPAN>* begin, <SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">const</SPAN>* end );
// Access methods
<SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">const</SPAN>* data() <SPAN class="reserv-word">const</SPAN>;
<SPAN class="cpp-type">size_t</SPAN> length() <SPAN class="reserv-word">const</SPAN>;
<SPAN class="cpp-type">bool</SPAN> is_empty() <SPAN class="reserv-word">const</SPAN>;
...
};</PRE>
<P class="first-line-indented">Now I am able to write a first test case - constructors
testing - and add it to a test suite. My test program became to look like this:</P>
<P>const_string_test.cpp:</P>
<PRE class="code">#<SPAN class="reserv-word">define</SPAN> BOOST_TEST_MODULE const_string test
#<SPAN class="reserv-word">include</SPAN> &lt;boost/test/unit_test.hpp&gt;
BOOST_AUTO_EST_CASE( constructors_test )
{
const_string cs0( <SPAN class="literal">&quot;&quot;</SPAN> ); <SPAN class="comment">// 1 //</SPAN>
BOOST_CHECK_EQUAL( cs0.length(), (<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">0</SPAN> );
BOOST_CHECK( cs0.is_empty() );
const_string cs01( NULL ); <SPAN class="comment">// 2 //</SPAN>
BOOST_CHECK_EQUAL( cs01.length(), (<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">0</SPAN> );
BOOST_CHECK( cs01.is_empty() );
const_string cs1( <SPAN class="literal">&quot;test_string&quot;</SPAN> ); <SPAN class="comment">// 3 //</SPAN>
BOOST_CHECK_EQUAL( std::strcmp( cs1.data(), <SPAN class="literal">&quot;test_string&quot;</SPAN> ), <SPAN class="literal">0</SPAN> );
BOOST_CHECK_EQUAL( cs1.length(), std::strlen(&quot;test_string&quot;) );
<SPAN class="cpp-type">std::string</SPAN> s( <SPAN class="literal">&quot;test_string&quot;</SPAN> ); <SPAN class="comment">// 4 //</SPAN>
const_string cs2( s );
BOOST_CHECK_EQUAL( std::strcmp( cs2.data(), <SPAN class="literal">&quot;test_string&quot;</SPAN> ), <SPAN class="literal">0</SPAN> );
const_string cs3( cs1 ); <SPAN class="comment">// 5 //</SPAN>
BOOST_CHECK_EQUAL( std::strcmp( cs1.data(), <SPAN class="literal">&quot;test_string&quot;</SPAN> ), <SPAN class="literal">0</SPAN> );
const_string cs4( <SPAN class="literal">&quot;test_string&quot;</SPAN>, <SPAN class="literal">4</SPAN> ); <SPAN class="comment">// 6 //</SPAN>
BOOST_CHECK_EQUAL( std::strncmp( cs4.data(), <SPAN class="literal">&quot;test&quot;</SPAN>, cs4.length() ), <SPAN class="literal">0</SPAN> );
const_string cs5( s.data(), s.data() + s.length() ); <SPAN class="comment">// 7 //</SPAN>
BOOST_CHECK_EQUAL( std::strncmp( cs5.data(), <SPAN class="literal">&quot;test_string&quot;</SPAN>, cs5.length() ), <SPAN class="literal">0</SPAN> );
const_string cs_array[] = { <SPAN class="literal">&quot;str1&quot;</SPAN>, <SPAN class="literal">&quot;str2&quot;</SPAN> }; <SPAN class="comment">// 8 //</SPAN>
BOOST_CHECK_EQUAL( cs_array[<SPAN class="literal">0</SPAN>], <SPAN class="literal">&quot;str1&quot;</SPAN> );
BOOST_CHECK_EQUAL( cs_array[<SPAN class="literal">1</SPAN>], <SPAN class="literal">&quot;str2&quot;</SPAN> );
}
<SPAN class="comment">// EOF</SPAN></PRE>
<P class="first-line-indented">The constructors_test test case is intended to check
a simple feature of the class const_string: an ability to construct itself properly
based on different arguments. To test this feature I am using such characteristics
of constructed object as a data it contains and a length. The specification of
the class const_string does not contain any expected failures, so, though the
constructor can fail if I would pass a pointer to an invalid memory, error check
control is not performed (can't require what was not promised :-)). But for any
valid input it should work. So I am trying to check a construction for an empty
string (1), a NULL string (2) a regular C string(3), an STL string(4), a copy
construction(5) and so on . Well, after fixing all the errors in the implementation
(do you write programs without errors from scratch?) I am able to pass this test
case and the unit test framework gives me the following report:</P>
<P class="test-output">Running 1 test case...<BR>
*** No errors detected</P>
<P class="first-line-indented">Encouraged I am moving on and adding more access
methods:</P>
<P>const_string.hpp</P>
<PRE class="code"><SPAN class="reserv-word">class</SPAN> const_string {
<SPAN class="reserv-word">public</SPAN>:
...
<SPAN class="cpp-type">char</SPAN> <SPAN class="reserv-word">operator</SPAN>[]( <SPAN class="cpp-type">size_t</SPAN> index ) <SPAN class="reserv-word">const</SPAN>;
<SPAN class="cpp-type">char</SPAN> at( <SPAN class="cpp-type">size_t</SPAN> index ) <SPAN class="reserv-word">const</SPAN>;
...
};
</PRE>
<P class="first-line-indented">I added the new feature - I need a new test case
to check it. As a result my test suite became to look like this:</P>
<P>const_string_test.cpp:</P>
<PRE class="code">#<SPAN class="reserv-word">define</SPAN> BOOST_TEST_MODULE const_string test
#<SPAN class="reserv-word">include</SPAN> &lt;boost/test/unit_test.hpp&gt;
BOOST_AUTO_EST_CASE( constructors_test )
{
...
}
BOOST_AUTO_EST_CASE( data_access_test )
{
const_string cs1( <SPAN class="literal">&quot;test_string&quot;</SPAN> ); <SPAN class="comment">// 1 //</SPAN>
BOOST_CHECK_EQUAL( cs1[(<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">0</SPAN>], <SPAN class="literal">'t'</SPAN> );
BOOST_CHECK_EQUAL( cs1[(<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">4</SPAN>], <SPAN class="literal">'_'</SPAN> );
BOOST_CHECK_EQUAL( cs1[cs1.length()-<SPAN class="literal">1</SPAN>], <SPAN class="literal">'g'</SPAN> );
BOOST_CHECK_EQUAL( cs1[(<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">0</SPAN>], cs1.at( <SPAN class="literal">0</SPAN> ) ); <SPAN class="comment">// 2 //</SPAN>
BOOST_CHECK_EQUAL( cs1[(<SPAN class="cpp-type">size_t</SPAN>)<SPAN class="literal">2</SPAN>], cs1.at( <SPAN class="literal">5</SPAN> ) );
BOOST_CHECK_EQUAL( cs1.at( cs1.length() - <SPAN class="literal">1</SPAN> ), <SPAN class="literal">'g'</SPAN> );
BOOST_CHECK_THROW( cs1.at( cs1.length() ), std::out_of_range ); <SPAN class="comment">// 3 //</SPAN>
}
<SPAN class="comment">// EOF</SPAN>
</PRE>
<P class="first-line-indented">In the data_access_test test case I am trying to
check the class const_string character access correctness. While tests (1) checks
valid access using const_string::operator[] and test (2) checks valid access using
method const_string::at(), there is one more thing to test. The specification
of the method const_string::at() contains validation for the out of bound access.
That was test (3) is intended to do: check that the validation is working. A testing
of a validation and error handling code is an important part of a unit testing
and should not be left for a production stage. The data_access_test test case
passed and I am ready for the next step.</P>
<P class="first-line-indented">Continuing my effort I am able to complete class
const_string (see <A href="const_string.hpp">Listing 1</A>) and
testing module for it (see <A href="const_string_test.cpp">Listing
2</A>) that is checking all features that are presented in the class const_string
specification.</P>
<P class="first-line-indented">Well, I am step closer to fulfilling my new
year resolution (we should see about this fitness club sometime next ...).
What about you? Your testing habits could be a little different. You could
start with a class/library development and then at some point start writing
test cases on feature basis. Or you can, given a detailed specification for
the future product, including expected interfaces, immediately start with
writing all test cases (or it could be a different person, while you working
on implementation at the same time). In any case you should not have any
problems to use facilities provided by the Boost.Test unit test framework
and, let me hope, be able to write a stable, bulletproof code. And what is
even more important is your confidence in an ability to make a changes of
any complexity without involving a lengthy regression testing of your whole
product. Your test module and the unit test framework will stay behind your
back to help you with any occasional errors.</P>
</DIV>
<DIV class="footer">
<DIV class="footer-body">
<P> &copy; <A name="Copyright">Copyright</A> <A href="mailto:boost-test%20at%20emailaccount%20dot%20com%20%28please%20unobscure%29">Gennadiy Rozental</A> 2001-2006. <BR>
Distributed under the Boost Software License, Version 1.0.
(See accompanying file <A href="../../../../../../LICENSE_1_0.txt">LICENSE_1_0.txt</A> or copy at
<A href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</A>)</P>
<P>Revised: <!-- #BeginDate format:Sw1 -->26 February, 2006<!-- #EndDate --> </P>
</DIV>
</DIV>
</BODY>
</HTML>