Development:Unit tests
Unit tests is a tool for developers that evaluates Moodle code and is found in the Administration block under Reports. The purpose of Unit Tests is to evaluate the individual parts of a program (functions, and methods of classes) to make sure that each element individually does the right thing. Unit Tests can be one of the first steps in a quality control process for developing or tweaking Moodle code. The next steps will involve other forms of testing to ensure that these different parts work together properly.
The unit testing framework is based on the SimpleTest framework. It was incorporated into Moodle by Nick Freear and Tim Hunt from The Open University.
Running the unit tests in Moodle
Running the basic tests
- Log in with an admin account.
- Go to the admin screen.
- Click on the Reports link near the bottom of the page.
- Click on the Run the unit tests link.
- Wait for the tests to run.
Options for running the tests
At the bottom of the tests page, there is form that lets you adjust the options used when running the tests.
Show passes as well as fails
Normally, only details of the tests that have failed are printed. Turning on this options shows details of all the passes too.
Show the search for test files
The tests to run are found automatically be searching the codebase for files whose names match test*.php in directories called simpletest. Turning on this option will print a list of the folders searched and the test files found. This is sometimes useful for debugging.
This option is particularly useful when one of your test files has a syntax error. When this happens, you sometimes just get a blank page with no error message. Turning on the show search option lets you see which test file it was that gave the error. If necessary, you can enable this option manually by adding "showsearch=1" to the end of the URL.
Run a thorough test (may be slow)
If you turn on this option, then as well as looking for files called test*.php, the search also looks for files called slowtest*.php.
To be useful, the full test run should find most bugs, but not take too long to complete. So if you have very, very detailed tests of an area of the code, it may be better to select a subset for everday testing, and only use the more detailed tests when a bug is reported, or you are doing new development in that area of the code.
This option is most useful when combined with the next option.
Only run tests in
Normally, tests from all parts of the codebase are run. However, when you are just doing development of one part of the code, that is a waste of time. You can type the name of a folder (for example mod/quiz) or a particular test file (for example lib/simpletest/testdatalib.php) and then only those tests will be run.
Instead of typing a path into this box, there is an easier way. Whenever a pass or fail is displayed, the name of the test file is printed. Each section of the path name is a link to run only the tests in that folder or file.
Writing new tests
As an example, suppose we wanted to start writing tests for the functions in the file 'question/editlib.php'.
Where to put the tests
If you have read the first half of this page and were paying attention, you can probably work out that you should create a folder called question/testeditlib, and create a file in there called something like testeditlib.php.
The skeleton of this file should look like:
<?php /** * Unit tests for (some of) question/editlib.php. * * @copyright © 2006 The Open University * @author T.J.Hunt@open.ac.uk * @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @package question */ /** */ require_once(dirname(__FILE__) . '/../../config.php'); global $CFG; require_once($CFG->libdir . '/simpletestlib.php'); // Include the test libraries require_once($CFG->dirroot . '/question/editlib.php'); // Include the code to test /** This class contains the test cases for the functions in editlib.php. */ class question_editlib_test extends UnitTestCase { function test_get_default_question_category() { // Do the test here/ } } ?>
That is, you have a class called something_test, and in that class you have lots of methods called test_something. Normally, you have one test method for each function you want to test, and you may as well called the test method test_name_of_function_being_tested.
Inside a test function
The inside of a test function tyically looks like this:
function test_get_default_question_category() { // Set things up in preparation for the test. // Call the function you want to test. // Check that the result is what you expected. }
For example:
Test data
TODO
setUp and tearDown methods
If all your test cases relate to the same area of code, and so need the same set of test data, then you can create a method called setUp()
that sets up the test data. If present, this method will be called before each test method. You can write a matching tearDown()
method if there is any clean-up that needs to be done after each test case has run.
If you have some test test cases the need one sort of setup, and some other test cases that need a different setup, consider splitting your tests into two separate classes, each with its own setUp()
method.
Further information
The simpletest documentation is at: http://simpletest.sourceforge.net/.
Changes to your existing code to make it work with unit testing
When code is being tested, it gets included from inside one of the simpletest library function. If the code is expecting to be run directly (for example, if it is a view.php or index.php function), you are likely to get errors because that expectation is no longer true.
Include paths
Includes like
require_once('../../config.php'); // Won't work.
won't work. Instead, the more robust option is
require_once(dirname(__FILE__) . '/../../config.php'); // Do this.
Access to global variables
Because your code was included from within a function, you can't access global variables until you have done a global statement.
require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/moodlelib.php'); // Won't work.
require_once(dirname(__FILE__) . '/../../config.php'); global $CFG; // You need this. require_once($CFG->libdir . '/moodlelib.php'); // Will work now.
Further reading about unit testing
The best book I know about unit testing is 'Pragmatic Unit Testing in Java with JUnit by Andrew Hunt (no relation) and David Thomas. I know, this book is not called Pragmatic Unit Testing in PHP with SimpleTest. However, it is an excellent book - short, to the point, and very practical. Most of what it says is not specific to Java and JUnit and it is obvious how to apply it in our testing setup.