Note: You are currently viewing documentation for Moodle 2.4. Up-to-date documentation for the latest stable version of Moodle may be available here: Unit tests.

Development:Unit tests

From MoodleDocs
Revision as of 12:15, 27 December 2007 by chris collman (talk | contribs) (Rewrite intro to state obvious :))

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

  1. Log in with an admin account.
  2. Go to the admin screen.
  3. Click on the Reports link near the bottom of the page.
  4. Click on the Run the unit tests link.
  5. 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.

RunOnlyTheseTests.png

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.