]>
Commit | Line | Data |
---|---|---|
1 | [/ | |
2 | / Copyright (c) 2003 Boost.Test contributors | |
3 | / | |
4 | / Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | /] | |
7 | ||
8 | [section:bt_and_tdd Test driven development with Boost.Test] | |
9 | ||
10 | 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 | |
11 | greasy food, start attending a fitness club and ... today I am going to test programs I am writing. I can start right | |
12 | after the last line of a program is completed or, even better, I can write tests while I am coding. And maybe next time | |
13 | I will write tests before the coding, during the design stage. I have read a lot of literature on how to write the | |
14 | tests, I have the unit test framework in hand and an idea of new class. So let's get started. | |
15 | ||
16 | Let say I want to encapsulate an unchangeable C character buffer with a length into the simple class `const_string`. | |
17 | Rationale: a string class that does not allocate a memory and provide a convenient read-only access to the preallocated | |
18 | character buffer. I will probably want `const_string` to have an interface similar to the class std::string. What will I | |
19 | do first? In my new life I will start with writing a test module for future class `const_string`. It will look like | |
20 | this: | |
21 | ||
22 | [import ../snippet/snippet13.cpp] [snippet13] | |
23 | ||
24 | 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 | |
25 | run the program it produces following output: | |
26 | ||
27 | ``*** No errors detected`` | |
28 | ||
29 | ||
30 | ||
31 | Well, now it could be a good time to start a work on `const_string`. First thing I imagine would be good to have is a | |
32 | constructors and trivial access methods. So my class initial version looks like this: | |
33 | ||
34 | [import ../snippet/snippet14.cpp] [snippet14] | |
35 | ||
36 | Now I am able to write a first test case - constructors testing - and add it to a test suite. My test program became to | |
37 | look like this: | |
38 | ||
39 | [import ../snippet/snippet15.cpp] [snippet15] | |
40 | ||
41 | The constructors_test test case is intended to check a simple feature of the class `const_string`: an ability to | |
42 | construct itself properly based on different arguments. To test this feature I am using such characteristics of | |
43 | constructed object as a data it contains and a length. The specification of the class `const_string` does not contain | |
44 | any expected failures, so, though the constructor can fail if I would pass a pointer to an invalid memory, error check | |
45 | control is not performed (can't require what was not promised :-)). But for any valid input it should work. So I am | |
46 | trying to check a construction for an empty string (1), a NULL string (2) a regular C string(3), an STL string(4), a | |
47 | copy construction(5) and so on. Well, after fixing all the errors in the implementation (do you write programs without | |
48 | errors from scratch?) I am able to pass this test case and the unit test framework gives me the following report: | |
49 | ||
50 | ``Running 1 test case... | |
51 | ||
52 | *** No errors detected | |
53 | `` | |
54 | ||
55 | Encouraged I am moving on and adding more access methods: | |
56 | ||
57 | [import ../snippet/snippet16.cpp][snippet16] | |
58 | ||
59 | 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: | |
60 | ||
61 | [import ../snippet/snippet17.cpp] [snippet17] | |
62 | ||
63 | In the data_access_test test case I am trying to check the class `const_string` character access correctness. While tests | |
64 | (1) checks valid access using `const_string::operator[]` and test (2) checks valid access using method | |
65 | `const_string::at()`, there is one more thing to test. The specification of the method `const_string::at()` contains | |
66 | validation for the out of bound access. That was test (3) is intended to do: check that the validation is working. A | |
67 | testing of a validation and error handling code is an important part of a unit testing and should not be left for a | |
68 | production stage. The data_access_test test case passed and I am ready for the next step. | |
69 | ||
70 | [import ../snippet/const_string.hpp] [import ../snippet/const_string_test.cpp] | |
71 | ||
72 | Continuing my effort I am able to complete class `const_string` (see [@../snippet/const_string.hpp Listing 1 | |
73 | =const_string.hpp=]) and testing module for it (see [@../snippet/const_string_test.cpp Listing 2 =const_string_test.cpp=]) | |
74 | that is checking all features that are presented in the class `const_string` specification. | |
75 | ||
76 | ||
77 | ||
78 | Well, I am step closer to fulfilling my new year resolution (we should see about this fitness club sometime next ...). | |
79 | What about you? Your testing habits could be a little different. You could start with a class/library development and | |
80 | then at some point start writing test cases on feature basis. Or you can, given a detailed specification for the future | |
81 | product, including expected interfaces, immediately start with writing all test cases (or it could be a different | |
82 | person, while you working on implementation at the same time). In any case you should not have any problems to use | |
83 | facilities provided by the Boost.Test unit test framework and, let me hope, be able to write a stable, bulletproof code. | |
84 | And what is even more important is your confidence in an ability to make changes of any complexity without involving a | |
85 | lengthy regression testing of your whole product. Your test module and the unit test framework will stay behind your | |
86 | back to help you with any occasional errors. | |
87 | ||
88 | ||
89 | [endsect] |