1.. index:: Test organization
2
3.. _Test_Organization:
4
5*****************
6Test Organization
7*****************
8
9
10.. _General_considerations:
11
12General considerations
13======================
14
15This section will discuss an approach to organizing an AUnit test harness,
16considering some possibilities offered by Ada language features.
17
18The general idea behind this approach to test organization is that making the
19test case a child of the unit under test gives some useful facilities.
20The test case gains visibility to the private part of the unit under test.
21This offers a more 'white box' approach to examining the state of the unit
22under test than would, for instance, accessor functions defined in a separate
23fixture that is a child of the unit under test. Making the test case a child of
24the unit under test also provides a way to make the test case share certain
25characteristics of the unit under test.  For instance, if the unit under test
26is generic, then any child package (here the test case) must be also generic:
27any instantiation of the parent package will require an instantiation of the
28test case in order to accomplish its aims.
29
30Another useful concept is matching the test case type to that of the unit
31under test, for example:
32
33* When testing a generic package, the test package should also be
34  generic.
35* When testing a tagged type, then test routines should be
36  dispatching, and the test case type for a derived tagged type should be a
37  derivation of the test case type for the parent.
38
39Maintaining such similarity of properties between the test case and unit under
40test can facilitate the testing of units derived in various ways.
41
42The following sections will concentrate on applying these concepts to
43the testing of tagged type hierarchies and to the testing of generic units.
44
45A full example of this kind of test organization is available in the AUnit
46installation directory:
47:samp:`{<AUnit-root>}/share/examples/aunit/calculator`, or
48from the AUnit source distribution
49:samp:`aunit-{<version>}-src/examples/calculator`.
50
51.. index:: OOP considerations (in test organization)
52
53.. _OOP_considerations:
54
55OOP considerations
56==================
57
58When testing a hierarchy of tagged types, one will often want to run tests
59for parent types against their derivations without rewriting those tests.
60
61We will illustrate some of the possible solutions available in AUnit,
62using the following simple example that we want to test:
63
64First we consider a ``Root`` package defining the ``Parent``
65tagged type, with two procedures ``P1`` and ``P2``.
66
67.. code-block:: ada
68
69   package Root is
70      type Parent is tagged private;
71
72      procedure P1 (P : in out Parent);
73      procedure P2 (P : in out Parent);
74   private
75      type Parent is tagged record
76         Some_Value : Some_Type;
77      end record;
78   end Root;
79
80We will also consider a derivation from type ``Parent``:
81
82.. code-block:: ada
83
84   with Root;
85   package Branch is
86      type Child is new Root.Parent with private;
87
88      procedure P2 (C : in out Child);
89      procedure P3 (C : in out Child);
90   private
91      type Child is new Root.Parent with null record;
92   end Branch;
93
94Note that ``Child`` retains the parent implementation of ``P1``,
95overrides ``P2`` and adds ``P3``. Its test will override
96``Test_P2`` when we override ``P2`` (not necessary, but certainly
97possible).
98
99.. index:: AUnit.Test_Fixtures.Test_Fixture type
100
101Using AUnit.Test_Fixtures
102-------------------------
103
104Using type ``Test_Fixture`, we first test ``Parent`` using the
105following test case:
106
107.. code-block:: ada
108
109   with AUnit; use AUnit;
110   with AUnit.Test_Fixtures; use AUnit.Test_Fixtures;
111
112   --  We make this package a child package of Parent so that it can have
113   --  visibility to its private part
114   package Root.Tests is
115
116      type Parent_Access is access all Root.Parent'Class;
117
118      --  Reference an object of type Parent'Class in the test object, so
119      --  that test procedures can have access to it.
120      type Parent_Test is new Test_Fixture with record
121         Fixture : Parent_Access;
122      end record;
123
124      --  This will initialize P.
125      procedure Set_Up (P : in out Parent_Test);
126
127      --  Test routines. If derived types are declared in child packages,
128      --  these can be in the private part.
129      procedure Test_P1 (P : in out Parent_Test);
130      procedure Test_P2 (P : in out Parent_Test);
131
132   end Root.Tests;
133
134.. code-block:: ada
135
136   package body Root.Tests is
137
138      Fixture : aliased Parent;
139
140      --  We set Fixture in Parent_Test to an object of type Parent.
141      procedure Set_Up (P : in out Parent_Test) is
142      begin
143         P.Fixture := Parent_Access (Fixture'Access);
144      end Set_Up;
145
146      --  Test routines: References to the Parent object are made via
147      --  P.Fixture.all, and are thus dispatching.
148      procedure Test_P1 (P : in out Parent_Test) is ...;
149      procedure Test_P2 (P : in out Parent_Test) is ...;
150
151end Root.Tests;
152
153The associated test suite will be:
154
155.. code-block:: ada
156
157   with AUnit.Test_Caller;
158   with Root.Tests;
159
160   package body Root_Suite is
161      package Caller is new AUnit.Test_Caller with (Root.Tests.Parent_Test);
162
163      function Suite return AUnit.Test_Suites.Access_Test_Suite is
164         Ret : Access_Test_Suite := AUnit.Test_Suites.New_Suite;
165      begin
166         AUnit.Test_Suites.Add_Test
167            (Ret, Caller.Create ("Test Parent : P1", Root.Tests.Test_P1'Access));
168         AUnit.Test_Suites.Add_Test
169            (Ret, Caller.Create ("Test Parent : P2", Root.Tests.Test_P2'Access));
170         return Ret;
171      end Suite;
172   end Root_Suite;
173
174Now we define the test suite for the ``Child`` type. To do this,
175we inherit a test fixture from ``Parent_Test``,
176overriding the ``Set_Up`` procedure to initialize ``Fixture`` with
177a ``Child`` object. We also override ``Test_P2`` to adapt it
178to the new implementation. We define a new ``Test_P3`` to test
179``P3``. And we inherit ``Test_P1``, since ``P1`` is unchanged.
180
181.. code-block:: ada
182
183   with Root.Tests; use Root.Tests;
184   with AUnit; use AUnit;
185   with AUnit.Test_Fixtures; use AUnit.Test_Fixtures;
186
187   package Branch.Tests is
188
189      type Child_Test is new Parent_Test with null record;
190
191      procedure Set_Up (C : in out Child_Test);
192
193      --  Test routines:
194      --  Test_P2 is overridden
195      procedure Test_P2 (C : in out Child_Test);
196      --  Test_P3 is new
197      procedure Test_P3 (C : in out Child_Test);
198
199   end Branch.Tests;
200
201.. code-block:: ada
202
203   package body Branch.Tests is
204      use Assertions;
205
206      Fixture : Child;
207      --  This could also be a field of Child_Test
208
209      procedure Set_Up (C : in out Child_Test) is
210      begin
211         --  The Fixture for this test will now be a Child
212         C.Fixture := Parent_Access (Fixture'Access);
213      end Set_Up;
214
215      --  Test routines:
216      procedure Test_P2 (C : in out Child_Test) is ...;
217      procedure Test_P3 (C : in out Child_Test) is ...;
218
219   end Branch.Tests;
220
221The suite for Branch.Tests will now be:
222
223.. code-block:: ada
224
225   with AUnit.Test_Caller;
226   with Branch.Tests;
227
228   package body Branch_Suite is
229      package Caller is new AUnit.Test_Caller with (Branch.Tests.Parent_Test);
230
231      --  In this suite, we use Ada 2005 distinguished receiver notation to
232      --  simplify the code.
233
234      function Suite return Access_Test_Suite is
235         Ret : Access_Test_Suite := AUnit.Test_Suites.New_Suite;
236      begin
237         --  We use the inherited Test_P1. Note that it is
238         --  Branch.Tests.Set_Up that will be called, and so Test_P1 will be run
239         --  against an object of type Child
240         Ret.Add_Test
241            (Caller.Create ("Test Child : P1", Branch.Tests.Test_P1'Access));
242         --  We use the overridden Test_P2
243         Ret.Add_Test
244            (Caller.Create ("Test Child : P2", Branch.Tests.Test_P2'Access));
245         --  We use the new Test_P2
246         Ret.Add_Test
247            (Caller.Create ("Test Child : P3", Branch.Tests.Test_P3'Access));
248         return Ret;
249      end Suite;
250   end Branch_Suite;
251
252Using AUnit.Test_Cases
253----------------------
254
255.. index:: AUnit.Test_Cases.Test_Case type
256
257Using an ``AUnit.Test_Cases.Test_Case`` derived type, we obtain the
258following code for testing ``Parent``:
259
260.. code-block:: ada
261
262   with AUnit; use AUnit;
263   with AUnit.Test_Cases;
264   package Root.Tests is
265
266      type Parent_Access is access all Root.Parent'Class;
267
268      type Parent_Test is new AUnit.Test_Cases.Test_Case with record
269         Fixture : Parent_Access;
270      end record;
271
272      function Name (P : Parent_Test) return Message_String;
273      procedure Register_Tests (P : in out Parent_Test);
274
275      procedure Set_Up_Case (P : in out Parent_Test);
276
277      --  Test routines. If derived types are declared in child packages,
278      --  these can be in the private part.
279      procedure Test_P1 (P : in out Parent_Test);
280      procedure Test_P2 (P : in out Parent_Test);
281
282   end Root.Tests;
283
284.. index:: AUnit.Test_Cases.Specific_Test_Case_Registration generic package
285
286The body of the test case will follow the usual pattern, declaring one or
287more objects of type ``Parent``, and executing statements in the
288test routines against them.  However, in order to support dispatching to
289overriding routines of derived test cases, we need to introduce class-wide
290wrapper routines for each primitive test routine of the parent type that
291we anticipate may be overridden. Instead of registering the parent's
292overridable primitive operations directly using ``Register_Routine``,
293we register the wrapper using ``Register_Wrapper``. This latter routine
294is exported by instantiating
295``AUnit.Test_Cases.Specific_Test_Case_Registration`` with the actual
296parameter being the parent test case type.
297
298.. code-block:: ada
299
300   with AUnit.Assertions; use AUnit.Assertions
301   package body Root.Tests is
302
303      --  Declare class-wide wrapper routines for any test routines that will be
304      --  overridden:
305      procedure Test_P1_Wrapper (P : in out Parent_Test'Class);
306      procedure Test_P2_Wrapper (P : in out Parent_Test'Class);
307
308      function Name (P : Parent_Test) return Message_String is ...;
309
310      --  Set the fixture in P
311      Fixture : aliased Parent;
312
313      procedure Set_Up_Case (P : in out Parent_Test) is
314      begin
315         P.Fixture := Parent_Access (Fixture'Access);
316      end Set_Up_Case;
317
318      --  Register Wrappers:
319      procedure Register_Tests (P : in out Parent_Test) is
320         package Register_Specific is
321            new Test_Cases.Specific_Test_Case_Registration (Parent_Test);
322         use Register_Specific;
323      begin
324         Register_Wrapper (P, Test_P1_Wrapper'Access, "Test P1");
325         Register_Wrapper (P, Test_P2_Wrapper'Access, "Test P2");
326      end Register_Tests;
327
328      --  Test routines:
329      procedure Test_P1 (P : in out Parent_Test) is ...;
330      procedure Test_P2 (C : in out Parent_Test) is ...;
331
332      --  Wrapper routines. These dispatch to the corresponding primitive
333      --  test routines of the specific types.
334      procedure Test_P1_Wrapper (P : in out Parent_Test'Class) is
335      begin
336         Test_P1 (P);
337      end Test_P1_Wrapper;
338
339      procedure Test_P2_Wrapper (P : in out Parent_Test'Class) is
340      begin
341         Test_P2 (P);
342      end Test_P2_Wrapper;
343
344   end Root.Tests;
345
346The code for testing the `Child` type will now be:
347
348.. code-block:: ada
349
350   with Parent_Tests; use Parent_Tests;
351   with AUnit; use AUnit;
352   package Branch.Tests is
353
354      type Child_Test is new Parent_Test with private;
355
356      function Name (C : Child_Test) return Message_String;
357      procedure Register_Tests (C : in out Child_Test);
358
359      --  Override Set_Up_Case so that the fixture changes.
360      procedure Set_Up_Case (C : in out Child_Test);
361
362      --  Test routines:
363      procedure Test_P2 (C : in out Child_Test);
364      procedure Test_P3 (C : in out Child_Test);
365
366   private
367      type Child_Test is new Parent_Test with null record;
368   end Branch.Tests;
369
370.. code-block:: ada
371
372   with AUnit.Assertions; use AUnit.Assertions;
373   package body Branch.Tests is
374
375      --  Declare wrapper for Test_P3:
376      procedure Test_P3_Wrapper (C : in out Child_Test'Class);
377
378      function Name (C : Child_Test) return Test_String is ...;
379
380      procedure Register_Tests (C : in out Child_Test) is
381         package Register_Specific is
382            new Test_Cases.Specific_Test_Case_Registration (Child_Test);
383         use Register_Specific;
384      begin
385         -- Register parent tests for P1 and P2:
386         Parent_Tests.Register_Tests (Parent_Test (C));
387
388         -- Repeat for each new test routine (Test_P3 in this case):
389         Register_Wrapper (C, Test_P3_Wrapper'Access, "Test P3");
390      end Register_Tests;
391
392      --  Set the fixture in P
393      Fixture : aliased Child;
394      procedure Set_Up_Case (C : in out Child_Test) is
395      begin
396         C.Fixture := Parent_Access (Fixture'Access);
397      end Set_Up_Case;
398
399      --  Test routines:
400      procedure Test_P2 (C : in out Child_Test) is ...;
401      procedure Test_P3 (C : in out Child_Test) is ...;
402
403      --  Wrapper for new routine:
404      procedure Test_P3_Wrapper (C : in out Child_Test'Class) is
405      begin
406         Test_P3 (C);
407      end Test_P3_Wrapper;
408
409   end Branch.Tests;
410
411Note that inherited and overridden tests do not need to be explicitly
412re-registered in derived test cases - one just calls the parent version of
413``Register_Tests``. If the application tagged type hierarchy is organized
414into parent and child units, one could also organize the test cases into a
415hierarchy that reflects that of the units under test.
416
417.. index:: Generic units (testing)
418
419.. _Testing_generic_units:
420
421Testing generic units
422=====================
423
424When testing generic units, one would like to apply the same generic tests
425to all instantiations in an application.  A simple approach is to make the
426test case a child package of the unit under test (which then must also be
427generic).
428
429For instance, suppose the generic unit under test is a package (it could
430be a subprogram, and the same principle would apply):
431
432.. code-block:: ada
433
434   generic
435      -- Formal parameter list
436   package Template is
437      -- Declarations
438   end Template;
439
440The corresponding test case would be:
441
442.. code-block:: ada
443
444   with AUnit; use AUnit;
445   with AUnit.Test_Fixtures;
446   generic
447   package Template.Gen_Tests is
448
449      type Template_Test is new AUnit.Test_Fixtures.Test_Fixture with ...;
450
451      --  Declare test routines
452
453   end Template.Gen_Tests;
454
455The body will follow the usual patterns with the fixture based on the
456parent package ``Template``. Note that due to an Ada AI, accesses to
457test routines, along with the test routine specifications, must be defined
458in the package specification rather than in its body.
459
460Instances of ``Template`` will automatically define the ``Tests`` child
461package that can be directly instantiated as follows:
462
463.. code-block:: ada
464
465   with Template.Gen_Test;
466   with Instance_Of_Template;
467   package Instance_Of_Template.Tests is new Instance_Of_Template.Gen_Test;
468
469The instantiated test case objects are added to a suite in the usual manner.
470
471