Unit Testing With MATLAB
mlUnit: MATLAB Unit Testing
Introduction
With the concept of Extreme Programming and Test-Driven Development and its technique of automated testing a number of testing frameworks for most programming languages were developed in the last years. The concept and architecture of all these frameworks are reaching back to SUnit, a unit test framework for the Smalltalk language published by Kent Beck in 1994. Due to its simplicity, caused amongst other things by the philosophy, that tests are written in the same language as the actual program, SUnit was ported to Java in 1998, creating JUnit. Today a whole family of these frameworks exists, called the XUnit family, e.g. CppUnit for C++, PyUnit for Python or NUnit for the .NET framework.
For the MATLAB language several unit testing frameworks were developed in the last months (in alphabetical order):
- MATUnit
- By Timothy Wall
- MUnit
- By Brad Phelan
- mlUnit
- By Thomas Dohmke
- munit
- By David Legland
- Unit Testing Tools
- By Kit Ng
This article describes the installation, usage and techniques of MUnit and mlUnit based on a comparison between both. It has to be said, that mlUnit is developed by the author of this article, so the article can't be fully objective (but I have tried my best).
Download
MUnit
- Link
- http://xtargets.com/cms/Downloads/cat_view-2.html
- Version
- 2.4
- Note
- MUnit requires MTemplate, so you have to download both packages.
mlUnit
- Link
- http://sourceforge.net/project/showfiles.php?group_id=166995
- Version
- 1.4
Installation
MUnit
- Run setups:
- MTemplate
- Start mtemplate-install-v1.4.exe and install MTemplate to $HOME/mtemplate.
- MUnit
- Start munit-install-v2.4.exe and install MUnit to $HOME/munit.
- Add directories to MATLAB path:
addpath('$HOME/mtemplate'); addpath('$HOME/munit');
mlUnit
- Unzip mlunit_1.3.zip to $HOME.
- Add directory to MATLAB path:
addpath('$HOME/mlunit/src');
Definition Of A Test Case
The definition of a test case with MUnit and mlUnit as shown in the following two example files are nearly the same. There are minor differences in the spelling of the fixture functions -- setup and set_up, teardown and tear_down -- in the creation of the object (line 3) and the invocation of the assert statement.
MUnit
- File test_munit.m
-
function test = test_munit test = munit_testcase; function setup end function teardown end function test_success x = 10; y = 10; test.assert(x == y); end function test_failure x = 10; y = 20; test.assert(x == y); end end
mlUnit
- File test_mlunit.m
-
function test = test_mlunit test = load_tests_from_mfile(test_loader); function set_up end function tear_down end function test_success x = 10; y = 10; assert(x == y); end function test_failure x = 10; y = 20; assert(x == y); end end
Running A Test Case
MUnit
Type at the MATLAB command prompt:
suite = munit_testsuite('test_munit');
r = suite.run();
r.web();
The command r.web(); opens the MATLAB web browser with the following page:
With edit the user can open the MATLAB editor with the respective test (file and line), with run (and debug) execute the test again (in debugging mode) and with Hide Details hide as well as display the details of failed tests.
mlUnit
Option A
Type at the MATLAB command prompt:
run(text_test_runner(1, 2), test_mlunit);
The results are written to the standard output or the given file handle (the first parameter of text_test_runner):
test_mlunit/test_success(function_test_case) ... OK test_mlunit/test_failure(function_test_case) ... FAIL ====================================================================== FAIL: test_mlunit/test_failure(function_test_case) ---------------------------------------------------------------------- Traceback (most recent call first): In p:\temp\test_mlunit.m at line 22 In p:\temp\mlunit\src\@function_test_case\run_test.m at line 11 In p:\temp\mlunit\src\@test_case\run.m at line 32 In p:\temp\mlunit\src\@test_suite\run.m at line 14 In p:\temp\mlunit\src\@text_test_runner\run.m at line 14 AssertionError: no message. ---------------------------------------------------------------------- Ran 2 tests in 0.031s FAILED (errors=0, failures=1)
Option B (new with Version 1.4)
Type at the MATLAB command prompt:
run(gui_test_runner, 'test_mlunit');
The command opens the following MATLAB figure, executes the tests and shows the result:
With Show the user can open the MATLAB editor with the respective failed test (file and line of failed assertion). With Run the test case can be executed again.
Additional Features
Both testing frameworks provide additional features, which are unique to each other.
MUnit
Bookmarking a Test Suite
MUnit allows on the front page of its user interface the creation of bookmarks to test suites.
Type at the MATLAB command prompt:
munit;
The user interface of MUnit is opened:
The bottom half presents a list of bookmarks to existing test suites, each bookmark with a link edit, run and remove. To add a new test suite to the list, simply type in the name of the test suite and press add.
Defining Assertions With Anonymous Functions or Constraints
The standard way of defining an assertion to compare two values is (nearly) the same with both frameworks:
test.assert(x == y);
The assert command of MUnit furthermore accepts an anonymous function handle as the input value:
test.assert(@() x == y);
The difference lies in the test report of a failed assertion. The following figure shows the test report of the above defined test method test_failure:
Now the assert statement of the test method is changed in that way, that it uses an anonymous function:
As you can see, the test report includes much more details about the reasons of the failed test. Therefore this is now the preferred way of defining assertions with MUnit.
With prior versions the recommended way of defining assertions was the use of constraints:
c = constraint; test.assert(c.eq(x, y));
The constraint object supports a number of operators, eg. eq, ne, and, or, isa or ask. The last one allows the definition of interactive tests, as the argument is shown in a dialog box with two buttons Yes and No. A click to No lets the test fail.
mlUnit
The Green Bar
As most popular testing frameworks the user interface of mlUnit provides a progress bar, which shows how many tests already have been executed and even more important, whether all of them were successful (green bar) or one of them failed (red bar). You can watch the progress bar in action, if you for example execute the unit tests of mlUnit itself:
addpath('$HOME/mlunit/test');
run(gui_test_runner, 'mlunit_all_tests');
Class-Based Testing
In addition to the above shown example, which defines all test methods within one .m-file, mlUnit allows also the traditional class-based definition of tests. The following steps explain the definition of the example with this:
- Create a folder for the class:
mkdir('@test_mlunit_cb'); - Change dir to the new folder:
cd('@test_mlunit_cb'); - Create an .m-file for the constructor of the class, which is inherited of test_case:
- File test_mlunit.m
-
function self = test_mlunit_cb(name) tc = test_case(name); self = class(struct([]), 'test_mlunit_cb', tc);
- Create an .m-file for each test method:
- File test_success.m
-
function self = test_success(self) x = 10; y = 10; assert(x == y);
- File test_failure.m
-
function self = test_failure(self) x = 10; y = 20; assert(x == y);
- Execute the test case:
cd('..'); run(gui_test_runner, 'test_mlunit_cb');
Summary
This article presented two testing frameworks for MATLAB: MUnit and mlUnit. MUnit has its focus on the management of different test suites, as it provides a list of bookmarked tests and the easy (one-click) access to all executed test methods. The major disadvantage of MUnit is the lack of a progress bar. When running a test suite with a lot of tests, e.g. taking 10 minutes, only the mouse cursor is switched to the hour glass providing no information, how many tests have passed or failed.
In constrast, the focus of mlUnit lies in the definition of test cases, either within one .m-file or as a class inherited from the base test_case. Furthermore the user interface of mlUnit provides a progress bar, but the test reports are less explanatory, as the use of anonymous functions or constraints is not intended for the definition of assertions.
Comments, Questions, Bugs
If you have a question, a comment or a bug report, please send me an email. Usually I try to answer within 24 hours. Spam and other crap goes automatically to the trash bin, so please use a precise subject.





