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