Differences between revisions 4 and 12 (spanning 8 versions)
Revision 4 as of 2011-10-18 06:23:12
Size: 6474
Editor: KaiJaeger
Comment:
Revision 12 as of 2012-11-29 17:04:11
Size: 11202
Editor: KaiJaeger
Comment:
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:

|| /!\ Work in progress ||
Line 19: Line 17:
Note that test case causing a crash are considered "broken" Test cases that do not get the expected result are considered "failing". Note that test cases causing a crash are considered "broken". Test cases that do not return the expected result are considered "failing".
Line 23: Line 21:
 1. The `#.Tester` class assumes that all your tests are hosted by an ordinary namespace. All methods take a ref to that namespace as an argument.
 1. All test functions inside that namespace are expected to start their names with the string `Test_` followed by digits.
 1. The `#.Tester` class assumes that all your tests are hosted by an ordinary namespace. All methods running test cases take a ref to that namespace as an argument.
 1. All test functions inside that namespace are expected to start their names with the string `Test_` followed by digits. '''Update''': since version 1.1 test functions can be grouped. For example, in order to group all functions testing a method `foo` the test functions are named `Test_foo_001` (or `Test_foo_01` or `Test_foo_00001`), `Test_foo_002`, `Test_foo_003` and so on.
Line 27: Line 25:
    * Run with error trapping and return a log (vector of text vectors) reporting details. Run this before checking in.
    * Run without error trapping. Use this for investigating why test cases unexpectedly fail.
    * Run "batch mode". It is assumed that those test cases that need a human being in front of the monitor are '''not''' executed when running in batch mode.
    * Run with error trapping and return a log (vector of text vectors) reporting details. Run this before checking your code into a Code Management System like !SubVersion. See `Tester.Run` for details.
    * Run without error trapping. Use this for investigating why test cases crash. See `Tester.RunDebug` for details.
    * Run "batch mode". It is assumed that those test cases that need a human being in front of the monitor are '''not''' executed when running in batch mode. See `Tester.RunBatchTests` for details. (Of course it is best not to rely on a human being but this cannot always be avoided)
Line 38: Line 36:
== Work flow ==

No matter which of the three main methods (`Run`, `RunBatchTests`, `RunDebug`) you are going to call, the workflow is always the same:

=== INI file s ===

First of all the `Run*` method checks whether there is a file `testcases_{computername}.ini`. If this is the case that INI file is processed. Use this to specify important computer-dependent variables.

It then checks whether there is a file `testcases.ini`. If this is the case that INI file is processed, too. Use this to specify general stuff that does not depend on a certain computer/environment.

Note that if one or both INI files exist there will be a flat namespace `{yourTestNamespace}.INI`, meaning that sections in the INI file are ignored. An example: if your test functions are hosted by a namespace "foo" and your INI file specifies a variable hello as holding "world" then:

{{{
       'world'≡#.foo.INI.hello
1
}}}

=== Initialisation ===

Now the `Run*` method checks whether there is a function "Initial" in the hosting namespace. If that is the case then the function is executed.

Note that the function must be either niladic or monadic and must not return a result. If the function is monadic then a Boolean is passed via the right argument telling the function that the test suite is going to run in batch mode if true.

Use this function to create stuff that's needed by '''all''' test cases, or tell the user something important - if the batch flag is false.

It might be a good idea to call a function tidying up in line 1, just in case a failing test case has left something behind; see below for details.

=== Finally: running the test cases ===

Now the test cases are executed one by one.

=== Tidying up ===

After the last test case was executed the `Run*` function checks whether there is a function "Cleanup" in the namespace hosting your test cases. If that's the case then this function is executed. Such a function must be a niladic, no-result returning function.

=== INI file again ===

Finally the namespace "INI" holding variables populated from your INI file(s) is deleted.

== Best Pratices ==

 * Try to keep your test cases simple and test just one thing at a time, if possible a method.
 * Create everything you need and tidy up afterwards. Or more precisely, tidy up (left overs!), create, test, tidy up again.
 * ....
Line 55: Line 98:
Returns the comment expected in line 1 of all test cases found in "refToTestNamespace". Returns the name and the comment expected in line 1 of all test cases found in "refToTestNamespace".
Line 80: Line 123:
Runs all test cases in "refToTestNamespace" <b>without</b> error trapping. If a test case encounters an invalid result it stops. Use this function to investigate the details after "Run" detected a problem. Runs all test cases in "refToTestNamespace" '''without''' error trapping. If a test case encounters an invalid result it stops. Use this function to investigate the details after "Run" detected a problem.
Line 84: Line 127:

{{{
      {log}←testCaseNos RunTheseIn refToTestNamespace
      {log}←(groupName testCaseNos) RunTheseIn refToTestNamespace
}}}

Like `RunDebug` but it runs just "testCaseNos" in "refToTestNamespace". Use this in order to debug one or some particular test cases.

If a "groupname" was specified then "testCaseNos" might be empty. Then all test cases of the given group are executed.
Line 134: Line 186:
 ----------------------------------------------------------------------------------------------
   26 test cases executed
  Test_MyGroup_002: Exercise MyGroup -2-
  Test_MyGroup_020: Exercise MyGroup -20-
  Test_MyGroup_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   29 test cases executed
Line 141: Line 196:

 2 10 #.Tester.RunTheseIn #.TestCases ⍝ Just test cases 2 and 10
--- Tests started at 2011-10-18 07:15:25 on #.TestCases -------------------------------------
  Test_002: Exercise "RmDir"
  Test_010: Exercise "DoesExist"
 ----------------------------------------------------------------------------------------------
   2 test cases executed
   0 test cases failed
   0 test cases broken

'MyGroup' (2 199 ) #.Tester.RunTheseIn #.TestCases ⍝ Test cases 2 and 199 of "MyGroup"
--- Tests started at 2011-10-18 07:15:25 on #.TestCases -------------------------------------
  Test_MyGroup_002: Exercise MyGroup -2-
  Test_MyGroup_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   2 test cases executed
   0 test cases failed
   0 test cases broken

 ('MyGroup' ⍬) #.Tester.RunTheseIn #.TestCases ⍝ All test cases of group "MyGroup"
--- Tests started at 2011-10-18 07:15:25 on #.TestCases -------------------------------------
  Test_002: Exercise MyGroup -2-
  Test_020: Exercise MyGroup -20-
  Test_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   3 test cases executed
   0 test cases failed
   0 test cases broken

Tester

(Hide table-of-contents)

Tester is part of the CategoryAplTree project.

Overview

The purpose of this class is to provide a framework for testing all the projects of the APLTree project. Only with such a framework is it possible to make changes to the test suite without too much hassle. You might find the framework flexible enough to suit your own needs when it comes to implementing tests.

Details

Terminology

Note that test cases causing a crash are considered "broken". Test cases that do not return the expected result are considered "failing".

Assumptions and preconditions

  1. The #.Tester class assumes that all your tests are hosted by an ordinary namespace. All methods running test cases take a ref to that namespace as an argument.

  2. All test functions inside that namespace are expected to start their names with the string Test_ followed by digits. Update: since version 1.1 test functions can be grouped. For example, in order to group all functions testing a method foo the test functions are named Test_foo_001 (or Test_foo_01 or Test_foo_00001), Test_foo_002, Test_foo_003 and so on.

  3. The first line after the header must carry a comment telling what is actually tested. Keep in mind that later you might be in need for finding out which test cases are testing a certain method!
  4. It is assumed that you have three different scenarios when you want to run test cases with a different behaviour:
    • Run with error trapping and return a log (vector of text vectors) reporting details. Run this before checking your code into a Code Management System like SubVersion. See Tester.Run for details.

    • Run without error trapping. Use this for investigating why test cases crash. See Tester.RunDebug for details.

    • Run "batch mode". It is assumed that those test cases that need a human being in front of the monitor are not executed when running in batch mode. See Tester.RunBatchTests for details. (Of course it is best not to rely on a human being but this cannot always be avoided)

  5. Every test function must accept a right argument which is a two-item vector of Booleans:
    1. stopFlag (0=use error trapping)

    2. batchFlag (0=does not run in batch mode)

  6. Every test function must return a scalar integer with one of:
    • 0: the test case is okay.

    • 1: the test case failed.

    • -1: means the test case did not execute itself because it is not designed to run in batch mode.

Work flow

No matter which of the three main methods (Run, RunBatchTests, RunDebug) you are going to call, the workflow is always the same:

INI file s

First of all the Run* method checks whether there is a file testcases_{computername}.ini. If this is the case that INI file is processed. Use this to specify important computer-dependent variables.

It then checks whether there is a file testcases.ini. If this is the case that INI file is processed, too. Use this to specify general stuff that does not depend on a certain computer/environment.

Note that if one or both INI files exist there will be a flat namespace {yourTestNamespace}.INI, meaning that sections in the INI file are ignored. An example: if your test functions are hosted by a namespace "foo" and your INI file specifies a variable hello as holding "world" then:

       'world'≡#.foo.INI.hello
1

Initialisation

Now the Run* method checks whether there is a function "Initial" in the hosting namespace. If that is the case then the function is executed.

Note that the function must be either niladic or monadic and must not return a result. If the function is monadic then a Boolean is passed via the right argument telling the function that the test suite is going to run in batch mode if true.

Use this function to create stuff that's needed by all test cases, or tell the user something important - if the batch flag is false.

It might be a good idea to call a function tidying up in line 1, just in case a failing test case has left something behind; see below for details.

Finally: running the test cases

Now the test cases are executed one by one.

Tidying up

After the last test case was executed the Run* function checks whether there is a function "Cleanup" in the namespace hosting your test cases. If that's the case then this function is executed. Such a function must be a niladic, no-result returning function.

INI file again

Finally the namespace "INI" holding variables populated from your INI file(s) is deleted.

Best Pratices

  • Try to keep your test cases simple and test just one thing at a time, if possible a method.
  • Create everything you need and tidy up afterwards. Or more precisely, tidy up (left overs!), create, test, tidy up again.
  • ....

Methods

Show the methods

GetAllTestFns

r←GetAllTestFns refToTestNamespace

Returns the names of all test functions found in namespace "refToTestNamespace".

ListTestCases

r←ListTestCases refToTestNamespace;list

Returns the name and the comment expected in line 1 of all test cases found in "refToTestNamespace".

Run

{log}←Run refToTestNamespace;flags

Runs all test cases in "refToTestNamespace" with error trapping. Broken as well as failing tests are reported in the session as such but they don't stop the program from carrying on.

RunBatchTests

{(rc log)}←RunBatchTests refToTestNamespace

Runs all test cases in "refToTestNamespace" but tells the test functions that this is a batch run meaning that test cases which are reporting to the session for any reason and those in need for a human being for interaction should quit silently. Returns 0 for okay or a 1 in case one or more test cases are broken or failed. This method can run in a runtime as well as in an automated test environment.

RunDebug

{log}←{stop}RunDebug refToTestNamespace

Runs all test cases in "refToTestNamespace" without error trapping. If a test case encounters an invalid result it stops. Use this function to investigate the details after "Run" detected a problem. This will work only if you use a particualar strategy when checking results in a test case

RunTheseIn

      {log}←testCaseNos RunTheseIn refToTestNamespace
      {log}←(groupName testCaseNos) RunTheseIn refToTestNamespace

Like RunDebug but it runs just "testCaseNos" in "refToTestNamespace". Use this in order to debug one or some particular test cases.

If a "groupname" was specified then "testCaseNos" might be empty. Then all test cases of the given group are executed.

Version

Returns version (x.y.z) and date (yyyy-mm-dd) of the class #.Tester

Examples

Show the examples

      )load APLTree\WinFile
Loaded....
      #.Tester.GetAllTestFns #.TestCases
 Test_001  Test_002  Test_003  Test_004  Test_005 ...
      ⊃#.Tester.ListTestCases #.TestCases
Exercise "MkDir"                           
Exercise "RmDir"                           
Exercise "Dir"                             
Exercise "DirX"                          
...
#.Tester.Run #.TestCases
--- Tests started at 2011-10-18 07:15:25  on #.TestCases -------------------------------------
  Test_001: Exercise "MkDir"
  Test_002: Exercise "RmDir"
  Test_003: Exercise "Dir"
  Test_004: Exercise "DirX"
  Test_005: Exercise "cd"
  Test_006: Exercise "Delete"
  Test_007: Exercise "DateOf"
  Test_008: Exercise "DoesExistDir"
  Test_009: Exercise "DoesExistFile"
  Test_010: Exercise "DoesExist"
  Test_011: Exercise "GetAllDrives"
  Test_012: Exercise "GetDriveAndType"
  Test_013: Exercise "IsDirEmpty"
  Test_014: Exercise "ListDirXIndices"
  Test_015: Exercise "ListFileAttributes"
  Test_016: Exercise "YoungerThan"
  Test_017: Exercise "GetTempFileName"
  Test_018: Exercise "GetTempPath"
  Test_019: Exercise "WriteAnsiFile" and "ReadAnsiFile"
  Test_020: Exercise "CopyTo"
  Test_021: Exercise "MoveTo"
  Test_022: Exercise "ListDirsOnly"
  Test_023: Exercise "CheckPath"
  Test_024: Exercise "PWD"
  Test_025: Exercise "IsFilenameOkay"
  Test_026: Exercise "IsFoldername"
  Test_MyGroup_002: Exercise MyGroup -2-
  Test_MyGroup_020: Exercise MyGroup -20-
  Test_MyGroup_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   29 test cases executed
   0 test cases failed
   0 test cases broken

      ⎕←↑#.Tester.RunBatchTests #.TestCases
0

 2 10 #.Tester.RunTheseIn #.TestCases    ⍝ Just test cases 2 and 10
--- Tests started at 2011-10-18 07:15:25  on #.TestCases -------------------------------------
  Test_002: Exercise "RmDir"
  Test_010: Exercise "DoesExist"
 ----------------------------------------------------------------------------------------------
   2 test cases executed
   0 test cases failed
   0 test cases broken

'MyGroup' (2 199 ) #.Tester.RunTheseIn #.TestCases    ⍝ Test cases 2 and 199 of "MyGroup"
--- Tests started at 2011-10-18 07:15:25  on #.TestCases -------------------------------------
  Test_MyGroup_002: Exercise MyGroup -2-
  Test_MyGroup_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   2 test cases executed
   0 test cases failed
   0 test cases broken

 ('MyGroup' ⍬) #.Tester.RunTheseIn #.TestCases    ⍝ All test cases of group "MyGroup"
--- Tests started at 2011-10-18 07:15:25  on #.TestCases -------------------------------------
  Test_002: Exercise MyGroup -2-
  Test_020: Exercise MyGroup -20-
  Test_199: Exercise MyGroup -199-
 ----------------------------------------------------------------------------------------------
   3 test cases executed
   0 test cases failed
   0 test cases broken

 4 #.Tester.RunDebug #.TestCases  ⍝ Stop at test case number 4
--- Tests started at 2011-10-18 07:16:03  on #.TestCases -------------------------------------
  Test_001: Exercise "MkDir"
  Test_002: Exercise "RmDir"
  Test_003: Exercise "Dir"

Run__[61]
⍝ Now you can trace into the forth test case.

Project Page

For bug reports, future enhancements and a full version history see Tester/ProjectPage

Version Information

Original author:

KaiJaeger

Responsible:

KaiJaeger

Email:

kai@aplteam.com


CategoryAplTree

Tester (last edited 2018-03-03 11:47:23 by KaiJaeger)