4. Automated tesing with the MTest toolbox

The MTest toolbox is based on object oriented programming as introduced in matlab version 7.6 (2008a). It is able to search a specified maindir (and subdirs) for files that include a predefined string (indicating that it is a test). It can transform the found testdefinition files (written in the format explained in the tutorial on testdefinitions into mtest objects containing all information that is in the testdefinition. Thest mtest objects can be run and published with the mtestengine. The following sections will show how.

Read more about automated testing

Contents

Building an mtestengine

Creating an mtestengine is as simple as the following code suggests:

mte = MTestRunner;

Similar to a struct we can now visualize the properties of this object:

mte
mte = 

  MTestRunner handle

  Properties:
            MainDir: 'e:\Temp\tp1c071d7c_df0d_4cef_af63_f7023d9bc749'
          Recursive: 1
            Publish: 0
            Verbose: 0
    IncludeCoverage: 1
          TargetDir: 'e:\Temp\tp1c071d7c_df0d_4cef_af63_f7023d9bc749'
             TestID: '_test'
         Exclusions: {'.svn'  '_tutorial'  '_exclude'}
           Template: 'default'
              Tests: [1x1 MTest]
      WrongTestDefs: {}
       FunctionsRun: {}


It directly shows the default options for all fields. Of course we would like to set some of the properties. This can be done either when creating the object itself (with property value pairs) or afterwards in the same way as altering fields of a struct:

mte.TargetDir = fullfile(tempdir,'htmltest');
mte.MainDir = fileparts(which('MTestRunner'));
mte.Verbose = true;
mte.Template = 'oet';

or:

mte = MTestRunner(...
    'TargetDir',fullfile(tempdir,'htmltest'),...
    'MainDir',fileparts(which('mte_simple_test')),...
    'Verbose',true,...
    'Template','oet');

Properties (fieldnames) of the MTestRunner

The MTestRunner has a couple of properties that can be used to define the behaviour of the MTestRunner when running and publishing. The following table lists the properties of an MTestRunner object.

Property Name

Class

Description

TargetDir

char Default=cd

Pathname of the directory where output (html) files must be generated.
MainDir

char Default=cd

Root directory of the toolbox that must be analysed and tested.
Recursive

logical Default=true

Flag to determine whether the mtestengine only lists tests in the maindir or also in all subdirs of the maindir.
Publish

logical Default=false

Determines whether the publishable parts of a test get published.
Verbose

logical Default=false

Flag that determines whether intermediate information must be printed in the command window.
IncludeCoverage

logical Default=true

Flag to turn on / off the profile function during execution of the tests to obtain function coverage information.
TestID

cell Default={'_test'}

The character arrays included in this cell determine which functionnames are identified as testdefinition files.
Exclusions

cell Default={'.svn','_tutorial'}

Any function or pathname that includes one of the character arrays filling this cell is excluded from the list of testsdefinitions.
Template

char Default=default

Name of the template that must be used to print the testresults of the toolbox. (OpenEarthTools has its own template named: "oet". Use this template when publising tests from OpenEarthTools).
Tests

mtest Default=[]

mtest object with the loaded testdefinitions. The mtestengine takes this list whenever the run command is given.
WrongTestDefs

cell Default={}

Files identified as tests according to the testid and exclusion keywords, but failed to load in an mtest object. This is probably due to a testdefinition that is not according to the convention used for the mtest toolbox.
FunctionsRun

mtestfunction Default=[]

Array of mtestfunction objects with information on the functions that were executed during the tests (including coverage information).

Setting properties is already adressed in the previous section.

Specifying tests to run

After creation of an mtestengine we can see that it does not contain any testdefinitions yet. The tests contains an empty test.

mte.Tests
ans = 

  MTest handle

  Properties:
              Name: []
          FileName: []
          FilePath: []
    FunctionHeader: ''
      FunctionName: ''
            H1Line: []
       Description: {}
            Author: []
           SeeAlso: {}
           Publish: 1
          MaxWidth: 600
         MaxHeight: 600
        StyleSheet: ''
            Ignore: 0
     IgnoreMessage: ''
          Category: 'Unit'
        TestResult: 0
              Time: 0
              Date: NaN
      ProfilerInfo: []
     FunctionCalls: []
        StackTrace: []
           Verbose: 1


manually add tests

Specifying tests can be done in two different ways. Of course we can add tests manually to the field:

mte.Tests(1) = MTest('mte_simple_test');

search for tests

The catalogueTests method (or function) searches for all testdefinitions that match the following requirements:

  • Tests should be in the maindir (or one of the subdirs if mte.recursive = true)
  • The filename should include one of the strings specified in the field testid of the mtestobject
  • The filename should not include one of the strings specified in the field exclusion of the mtestobject
  • mfiles that match the above mentionned criteria are converted to mtest objects. they must therefore match the layout criteria described in the tutorial on testdefinitions

By default testdefinition files include the following string in their filename:

mte.TestID
ans =

_test

but we exclude files with one of these strings in the filename

mte.Exclusions
ans = 

    '.svn'    '_tutorial'    '_exclude'

After automatically searching for testdefinitions and adding them to the testengine ...

cataloguetests(mte);
Collecting tests

we can see that the content of the tests field has changed. This is of course due to the fact that we just collected all tests that match the description as stated above:

mte
mte = 

  MTestRunner handle

  Properties:
            MainDir: 'F:\OpenEarthTools\matlab\maintenance\1_automated_Testing\examples'
          Recursive: 1
            Publish: 0
            Verbose: 1
    IncludeCoverage: 1
          TargetDir: 'e:\Temp\htmltest'
             TestID: '_test'
         Exclusions: {'.svn'  '_tutorial'  '_exclude'}
           Template: 'oet'
              Tests: [1x11 MTest]
      WrongTestDefs: {'F:\OpenEarthTools\matlab\maintenance\1_automated_Testing\examples\mte_wrongdefinition_test.m'  [1x1 MException]}
       FunctionsRun: {}


Run all tests

To run all tests specified in the tests field of the mtestengine object there is a very simple function:

run(mte);
## start running tests ##
     mte_wrongdefinition_test Could not be interpreted as a valid testdefinition.
 1. mt_definitionwithdescription_newstyle_test (mt_definitionwithdescription_newstyle_test)
     Test started: mt_definitionwithdescription_newstyle_test
     Test finished: mt_definitionwithdescription_newstyle_test
 2. name of the test (mte_concepttest_test)
     Test started: name of the test
     Error occurred: 1 is not 2
     Test finished: name of the test
 3. testname (mte_fulldefinition_test)
     Test started: testname
     Test finished: testname
 4. mte_givecategory_test (mte_givecategory_test)
     Test started: mte_givecategory_test
     Test finished: mte_givecategory_test
 5. name of the test (mte_givename_test)
     Test started: name of the test
     Test finished: name of the test
 6. mte_headeronly_test (mte_headeronly_test)
     Test started: mte_headeronly_test
     Test finished: mte_headeronly_test
 7. Ignore test (mte_ignore_test)
     Test started: Ignore test
     Ignored: ignore test example
     Test finished: Ignore test
 8. mte_simple_test (mte_simple_test)
     Test started: mte_simple_test
     Test finished: mte_simple_test
 9. mte_simplest_test (mte_simplest_test)
     Test started: mte_simplest_test
     Test finished: mte_simplest_test
 10. mte_testpublish_test (mte_testpublish_test)
     Test started: mte_testpublish_test
     Test finished: mte_testpublish_test
 11. mte_versiononly_test (mte_versiononly_test)
     Test started: mte_versiononly_test
     Test finished: mte_versiononly_test

Each mtest object under the field "tests" contains a field that stores the testresult:

mte.Tests(2).TestResult
ans =

     0

Run all tests and publish documentation and results

The run function runs all tests and publishes the description and vizualization of results as specified in the testdefinition files. TODO: It also publishes the coverage of the tests (which percentages (and lines) of which functions did we actually test). More information on this function can be found in its help documentation:

help MTestRunner.run
 RUN  Runs all tests (that are in the "Test" property)
 
    This function executes the run function of all mtest objects in the Tests property.
 
    TODO:
        - include input argument to specify test numbers (instead of just all tests).
 
    Syntax:
    outobj = obj.run;
    outobj = run(obj);
 
    Input:
    obj     -   an MTestRunner object.
 
    Output:
    outobj  -   The same MTestRunner object as the input argument obj. It is not 
                necessary to have an output, since the MTestRunner is of type handle. 
                This automatically adjusts all copies of the object that are in the 
                matlab memory. The one that is in the base workspace is therefore 
                automatically updated and does not need to be output of the function.
 
    See also MTestRunner MTestRunner.MTestRunner MTestRunner.run MTest MTestFactory TeamCity

Publication templates

Publication of the mtestengine results is done acoording to a predefined format. This format is determined by the choice for a template. It is of course also possible to write your own template. At this moment there is not yet a tutorial on how to write such a template. The (sometimes hidden) functions of the MTestRunner object give a lot of information already.