1<HTML>
2<!-- $Id: UnitTestingInfo.html 10 2002-07-09 12:24:59Z ejakowatz $ -->
3<HEAD>
4<TITLE>Unit Testing Information</TITLE>
5</HEAD>
6
7<BODY BGCOLOR="white" LINK="#000067" VLINK="#000067" ALINK="#0000FF">
8<FONT FACE="Verdana,Arial,Helvetica,sans-serif" SIZE="-1">
9
10<H1>Unit Testing Information:</H1>
11
12<P>This document describes the "why's" and "how's" of unit testing for the AppKit team of the
13OpenBeOS project.  Although it is intended for the AppKit team, there is no reason other teams
14couldn't use this information to develop a similar unit testing strategy.</P>
15
16<P>The document has the following sections:</P>
17
18<OL>
19<LI><A HREF="#what">What is unit testing?</A></LI>
20<LI><A HREF="#why">Why is unit testing important?</A></LI>
21<LI><A HREF="#when">When should I write my unit tests?</A></LI>
22<LI><A HREF="#whattests">What kinds of tests should be in a unit test?</A></LI>
23<LI><A HREF="#testframework">What framework is being used to do unit testing for the AppKit?</A></LI>
24<LI><A HREF="#frameworkmods">What AppKit specific modifications have been made to this framework?</A></LI>
25<LI><A HREF="#futuremods">What framework modifications might be required in the future?</A></LI>
26<LI><A HREF="#buildingtests">How do I build the framework and current tests for the AppKit?</A></LI>
27<LI><A HREF="#runningtests">How do I run tests?</A></LI>
28<LI><A HREF="#writingtests">How do I write tests for my component?</A></LI>
29<LI><A HREF="#exampletests">Are there example tests to base mine on?</A></LI>
30<LI><A HREF="#threadedtests">How do I write a test with multiple threads?</A></LI>
31</OL>
32
33<A NAME="what"></A><H2>What is unit testing?</H2>
34
35<P>Unit testing is the process of showing that a part of a software system works as far as the
36requirements created for that part of the system.  Unit testing is best if it has the following
37characteristics:</P>
38
39<UL>
40<LI>The software component is tested in isolation with as little interaction with other software
41components as possible.</LI>
42<LI>The software component is tested using automated tools so that unit tests can be run with
43every build of the software if required.</LI>
44<LI>All requirements of the software component are tested as part of the unit tests.</LI>
45</UL>
46
47<P>Unit testing is not the only type of testing but is definitely a very important part of any
48testing strategy.  Following unit testing, software should go through "integration testing" to
49show that the components work as expected when put together.</P>
50
51<A NAME="why"></A><H2>Why is unit testing important?</H2>
52
53<P>A basic concept of software engineering is that the cost of fixing a bug goes up by a factor
54of 2-10x (depending on the source of the information) the later in the development process it is
55found.  Unit testing is critical to finding implementation bugs within a particular component as
56quickly as possible.</P>
57
58<P>Unit testing will also help to find requirements problems also.  If you write the requirements
59(or use cases) for your component from the BeBook, hopefully the BeBook and your use cases will
60match the actual Be implementation.  A good way to confirm that the BeBook documentation matches
61Be's implementation is to write your unit tests and run them against the original Be code.</P>
62
63<P>Unit tests will also continue to be maintained and run in the future also.  As the mailing 
64lists obviously show, many people are looking forward to OpenBeOS post-R1 when new features
65will be introduced above and beyond BeOS R5.  These unit tests will be critical to ensuring that
66any new feature or even just a bug fix doesn't break existing functionality.</P>
67
68<P>Speaking of bug fixes, consider adding unit tests for any bugs you identify that slipped
69through your original unit test suite.  This will ensure that this bug or a similar one is not
70re-introduced in the future.</P>
71
72<P>Finally, unit testing is not the be all, end all of testing.  As mentioned above, integration
73testing must be done to show that software components work together.  If all unit tests cover
74all requirements and have run successfully against all components, then a failure has to be
75due to a bug in the interaction of two or more known working software components.</P>
76
77<A NAME="when"></A><H2>When should I write my unit tests?</H2></LI>
78
79<P>As the AppKit process document describes the recommended order for implementing a component
80is:</P>
81
82<OL>
83<LI>Write an interface specification</LI>
84<LI>Write the use case specifications</LI>
85<LI>Write the unit tests</LI>
86<LI>Write an implementation plan</LI>
87<LI>Write the code</LI>
88</OL>
89
90<P>Please see the AppKit process document for more details about the entire sequence.  The unit
91test are to be written once the use cases are written and before any implementation work is
92done.  The use cases must be done because they determine what the tests will be.  You need to
93write as many tests are required so that all use cases for that component are tested.  The use
94cases should be detailed enough that you can write your unit tests from them.</P>
95
96<P>The unit tests are to be done before implementation for a very good reason.  You should be able
97to run these unit tests against the Be implementation and confirm that they all pass.  If they do
98not pass, then either there is a bug in the unit test itself or you have found a difference
99between your use cases and the actual implementation.  Even if your use cases match the BeBook,
100if that is not how the actual Be implementation works, we must match the current implementation and
101not the BeBook.  You should go back and modify the use case.  Change the use case so that it
102matches Be's implementation and consider adding a note indicating this doesn't match the 
103BeBook.</P>
104
105<P>Imagine if you completed the implementation and then wrote and ran the unit tests.  If you run
106the tests against your implementation and Be's implementation, you will notice the test passes
107for your code but fails on Be's.  At this point, you will have to change the implementation, change
108the unit test and change the use case which is more work that if you write the unit tests before
109the implementation.  Worse, if you only ran the unit tests against your implementation and not
110Be's, you may not notice the problem at all.</P>
111
112<A NAME="whattest"></A><H2>What kinds of tests should be in a unit test?</H2>
113
114<P>The unit tests you write should cover all the functionality of your software module.  That
115means your unit tests should include:</P>
116
117<UL>
118<LI>All standard expected functionality of the software component</LI>
119<LI>All error conditions handled by the software component</LI>
120<LI>Interaction with software components which cannot be decoupled from the target software 
121component</LI>
122<LI>Concurrency tests to show that a software component which is expected to be thread safe (most
123things are under BeOS) is safe and free from deadlocks.</LI>
124</UL>
125
126<A NAME="testframework"></A><H2>What framework is being used to do unit testing for the AppKit?</H2>
127
128<P>The AppKit team has chosen to use CppUnit version 1.5 as the basis of all of our unit tests.
129This framework provides very useful features and ensures that all unit tests for AppKit code
130are consistent and can be executed from a single environment.</P>
131
132<P>There are two key components to the framework.  First, there is a library called libCppUnit.so
133which provides all the C++ classes for defining your own testcases.  Secondly, there is an
134executable called "TestRunner" which is capable of executing a set of testcases.</P>
135
136<P>For more information on CppUnit, please refer to 
137<A HREF="http://cppunit.sourceforge.net/">this website</A>
138
139<A NAME="frameworkmods"></A><H2>What AppKit specific modifications have been made to this framework?</H2>
140
141<P>The following are the modifications that have been introduced into the CppUnit v1.5 
142framework:</P>
143
144<UL>
145<LI>A makefile has been added for the library and the TestRunner.</LI>
146<LI>Some "bugs" in CppUnit v1.5 which lead to it not compiling under BeOS v5.</LI>
147<LI>The TestRunner has been modified to support BeOS based addons.  Each test which you can
148select from the TestRunner is found in the "add-ons" directory at runtime.  The original 
149TestRunner required you to change the TestRunner when new tests were added to it.</LI>
150<LI>Changed the output from TestRunner.  The output includes a name of the test being run and
151a run time for the test in microseconds.</LI>
152<LI>Changed the arguments of the assert functions in the TestCase class from std::string to
153const char *'s due to apparent concurrency problems with std::string under BeOS when testing
154threaded tests.</LI>
155<LI>Added locking to the TestResults class so that multiple threads can safely add result 
156information at the same time for a single test.</LI>
157<LI>The ThreadedTestCaller class was written to allow us to write tests which contain multiple
158threads.  This is an important class because many BeOS components are thread safe and we need
159to confirm that the OpenBeOS implementation is also thread safe.</LI>
160</UL>
161
162<P>This is the list of the important modifications done to CppUnit v1.5 at the time this document
163is being written.  For the latest information about modifications to CppUnit, check the code
164which can be found in the OpenBeOS CVS repository.</P>
165
166<A NAME="futuremods"></A><H2>What framework modifications might be required in the future?</H2>
167
168<P>This framework will have to evolve as our needs grow.  The main issues I think we need to
169solve are:</P>
170
171<UL>
172<LI>The format of the test name is an encoded string representing the class definition of the
173test class from gcc.  It is not a very readable format but given that the test class is often
174a template class and you would like different names for different instances of the template,
175this seemed the best compromise.  Suggestions welcome.</LI>
176
177<LI>The threaded test support added into CppUnit forces you to specify the entry point for each
178thread in your test.  If you are doing a test with a BLooper or a BWindow, these classes start
179a thread of their own.  This thread will not be started through the standard entry point so
180doing "assert's" from one of these threads will not work.  Perhaps we need TestBLooper and
181TestBWindow classes which will work with the assert's.</LI>
182</UL>
183
184<P>If you find you need some other features, feel free to add them to CppUnit.</P>
185
186<A NAME="buildingtests"></A><H2>How do I build the framework and current tests for the AppKit?</H2>
187
188<P>As of writing this document, you can build the framework and all the current AppKit tests
189by performing the following steps:</P>
190
191<OL>
192
193<LI>Checkout the "app_kit" sources or the entire repository from the OpenBeOS CVS repository.
194There is information at the OpenBeOS site about how to access the CVS repository.</LI>
195
196<LI>In a terminal, "cd" into the "app_kit" directory in the CVS files you checked out.</LI>
197
198<LI>Type "make".</LI>
199
200</OL>
201
202<P>Note that the build system for OpenBeOS is moving to jam so these steps may become obsolete.
203When you the make has finished, you should find the following files:</P>
204
205<UL>
206
207<LI><CODE>app_kit/test/CppUnit/TestRunner</CODE> - this is the executable to use to execute
208tests.</LI>
209<LI><CODE>app_kit/test/CppUnit/lib/libCppUnit.so</CODE> - this is CppUnit library which your tests
210must link against.</LI>
211<LI><CODE>app_kit/test/CppUnit/lib/libopenbeos.so</CODE> - this is library which contains OpenBeOS
212implementation of some Be classes (usually found in libbe.so, called libopenbeos.so to avoid a name
213clash at runtime).</LI>
214<LI><CODE>app_kit/test/add-ons/BAutolockTests</CODE> - this is the addon which contains the tests
215which are run against the Be and OpenBeOS implementation of BAutolock.</LI>
216<LI><CODE>app_kit/test/add-ons/BLockerTests</CODE> - this is the addon which contains the tests
217which are run against the Be and OpenBeOS implementation of BLocker.</LI>
218<LI><CODE>app_kit/test/add-ons/BMessageQueueTests</CODE> - this is the addon which contains the tests
219which are run against the Be and OpenBeOS implementation of BMessageQueue.</LI>
220
221</UL>
222
223<P>These are the key files which ensure that the tests can be run.</P>
224
225<A NAME="runningtests"></A><H2>How do I run tests?</H2>
226
227<P>You have a few different options for how you run a test or a series of tests.  Before you start
228however, you must build the code as describe <A HREF="#buildingtests">in this section</A>.  Once
229it is built, you can run tests any of these ways:</P>
230
231<UL>
232<LI>Run "make test" from the app_kit directory.  This will lead to all of the tests defined in
233app_kit/test/add-ons directory to be run.</LI>
234<LI>From the "app_kit/test" directory, execute the command "CppUnit/TestRunner -all".  This will
235lead to all of the tests defined in the app_kit/test/add-ons directory to be run and is the same
236as what happens in the "make" example above.  However, recompile any code that has changed in the
237process.</LI>
238<LI>From the "app_kit/test" directory, execute the command "CppUnit/TestRunner &lt;TestName&gt;"
239where &lt;TestName&gt; is one of the addons found in the "app_kit/test/add-ons" directory.  Only
240the tests defined in that add-on will be run.</LI>
241</UL>
242
243<A NAME="writingtests"></A><H2>How do I write tests for my component?</H2>
244
245<P>The first step to writing your tests is to develop a plan for how you will test the 
246functionality.  For ideas of the kinds of tests you may want to consider, you should reference
247<A HREF="#whattests">this section</A>.</P>
248
249<P>Once you know the kinds of tests you want, you need to:</P>
250
251<UL>
252
253<LI><P>For every test you want, define a class which derives from the "TestCase" class in the 
254CppUnit framework.</P></LI>
255
256<LI><P>Within each test class you define, create a "void setUp(void)" and "void tearDown(void)"
257member function if required.  If before executing your test, you need to perform some actions,
258put those actions in the "setUp()" member.  If you need to cleanup after your test, put those
259actions in the "tearDown()" member.</P></LI>
260
261<LI><P>Within each test class you define, create a member function which takes "void" and
262returns "void".  Within this member function, write the code to execute the test.  Whenever you
263want to ensure that some condition is true during your test, add a line within the member function
264that looks like "assert(condition)".  For example, if the variable "result" must have the value
265B_OK at a particular point in your test, you should add a line which reads
266"assert(result = B_OK)".</P></LI>
267
268<LI><P>Create a constructor for all of your test classes that takes a "std::string name" argument
269and pass that onto the TestCase parent class.  Add whatever actions you need to take in the
270constructor.</P></LI>
271
272<LI><P>Create a destructor for all of your test classes and take whatever actions are 
273appropriate.</P></LI>
274
275<LI><P>Within each test class you define, create a member with the signature 
276"static Test *suite(void)".  For a simple test where only one test needs to be run for this class,
277the contents of this member should look like:</P>
278
279<PRE>
280return(new TestCaller&lt;ClassName&gt;("", &amp;ClassName::MemberName));
281</PRE>
282
283<P>Replace "ClassName" with the name of your test class and "MemberName" with the name
284of the member function you defined your test in.  If you need to define more than one test to run
285from this class, refer to instructions below on how to use the TestSuite class of CppUnit.  If you
286are creating a threaded test, refer to <A HREF="#threadedtests">this section</A>.</P></LI>
287
288<LI><P>Create one ".cpp" file for defining the "addonTestFunc()" function.  This function must
289exist in global scope within your test addon.  The contents of this ".cpp" file will look something
290like:</P>
291
292<PRE>
293#include "TestAddon.h"
294
295Test *addonTestFunc(void)
296{
297	TestSuite *testSuite = new TestSuite("&lt;TestSuiteName&gt;");
298
299	testSuite-&gt;addTest(&lt;ClassName1&gt;::suite());
300	testSuite-&gt;addTest(&lt;ClassName2&gt;::suite());
301	/* etc */
302
303	return(testSuite);
304}
305</PRE>
306
307<P>In the above example, replace &lt;TestSuiteName&gt; with an appropriate name for the group of
308tests and &lt;ClassName1&gt; and &lt;ClassName2&gt; with the names of the test classes you have
309defined.</P></LI>
310
311<LI><P>Create a build system around a BeIDE project, Makefile or preferrably a jam file which
312builds all the necessary code you have written into an addon.</P></LI>
313
314<LI><P>Put this addon into the app_kit/test/add-ons directory and follow the above instructions
315for how to run your tests.</P></LI>
316
317</UL>
318
319<A NAME="exampletests"></A><H2>Are there example tests to base mine on?</H2>
320
321<P>There are example tests which you can find in the following directories:</P>
322
323<UL>
324<LI><CODE>app_kit/test/lib/application/BMessageQueue</CODE></LI>
325<LI><CODE>app_kit/test/lib/support/BAutolock</CODE></LI>
326<LI><CODE>app_kit/test/lib/support/BLocker</CODE></LI>
327</UL>
328
329<P>There are some things done in these tests which make things a bit more complex, but you may
330want to do similar things:</P>
331
332<UL>
333
334<LI>Most tests use a ThreadedTestCaller class even in some situations when there aren't actually
335more than one thread in the test.</LI>
336
337<LI>All tests are defined as a template class.  The test class is a template of the class to test
338(if that makes sense to you).  For example, to test both the Be and OpenBeOS BLocker and not
339end up with a symbol conflict, the OpenBeOS implementation of BLocker is actually in a namespace
340called "OpenBeOS".  So, the tests must be run against the classes "::BLocker" and 
341"OpenBeOS::BLocker".  The easiest way to do this was to make the class to be tested a template
342and define it for both "::BLocker" and "OpenBeOS::BLocker".</LI>
343
344</UL>
345
346<P>Even with the complexity, I think this code provides a pretty good example of how to write
347your tests.</P>
348
349<A NAME="threadedtests"></A><H2>How do I write a test with multiple threads?</H2>
350
351<P>If you have a test which you want to define that requires more than one thread of execution
352(most likely a concurrency test of you code), you need to use the ThreadedTestCaller class.
353The steps which differ from the above description on how to write a test case are:</P>
354
355<UL>
356
357<LI><P>In your test class, define a member function for each thread you will be starting.  All of
358these member functions must take "void" and return "void".  If all the threads in your test
359perform the exact same actions, it is OK to just define one member function.  Usually in the
360tests I have written, I have called these member functions "TestThread1()", "TestThread2()",
361etc.</P></LI>
362
363<LI><P>If your "static Test *suite()" function for your test class, you must return a
364ThreadedTestCaller.  Imagine that the test class name is "MyTestClass" and you want two threads
365which run member functions "TestThread1()" and "TestThread2()".  That code would look like:</P>
366
367<PRE>
368Test *MyTestClass::suite(void)
369{
370	MyTestClass *theTest = new MyTestClass("");
371	ThreadedTestCaller&lt;MyTestClass&gt; *threadedTest = new TreadedTestCaller&lt;MyTestClass&gt;("", theTest);
372
373	threadedTest-&gt;addThread(":Thread1", &amp;MyTestClass::TestThread1);
374	threadedTest-&gt;addThread(":Thread2", &amp;MyTestClass::TestThread2);
375
376	return(threadedTest);
377}
378</PRE>
379
380<P>If you need to, you can put a number of ThreadedTestCaller instances into a TestSuite and return
381them in the suite() member function.  Examples of this can be found in the BLocker and 
382BMessageQueue test examples.</P></LI>
383
384</UL>
385
386<P>Otherwise the steps are the same as for other tests.  The code gets much more complex if you
387define your test classes as templates as the examples do.</P>
388
389</FONT>
390</BODY>
391</HTML>
392