Difference between revisions of "Unit Testing Best Practices"

From Joomla! Documentation

Line 12: Line 12:
 
public function bar($string)
 
public function bar($string)
 
{
 
{
if(is_int($string))
+
if (is_int($string))
 
{
 
{
 
return false;
 
return false;

Revision as of 05:38, 18 October 2014

Introduction[edit]

This article lists a few best practices related to writing unit tests for Joomla. These best practices have not been implemented in all tests of the existing testsuite yet, so if you find a place where this is not implemented in this way consider to change it and implement it.

One test per method[edit]

Make sure, that each test method tests exactly one thing. This makes it much easier to find out what causes a failed test, makes test methods more readable and also gives more meaningful Testdox outputs (see below).

Let's assume we want to test this class:

<?php
class Foo
{
	public function bar($string)
	{
		if (is_int($string))
		{
			return false;
		}

		$string = strtoupper($string);

		return $string;
	}
}

Below you can find a bad example of a test for this method, because it tests two different things (convert to uppercase, return false on int) in the same method:

<?php
class FooTest extends PHPUnit_Framework_Testcase
{
	public function testBar()
	{
		$object = new foo();

		$this->assertEquals('EXAMPLE', $object->bar('example'));
		$this->assertEquals(false, $object->bar(4));
	}
}

A better approach would be to split these two assertions into two test methods:

<?php
class FooTest extends PHPUnit_Framework_Testcase
{
	public function testBar1()
	{
		$object = new foo();

		$this->assertEquals('EXAMPLE', $object->bar('example'));
	}

	public function testBar2()
	{
		$object = new foo();

		$this->assertEquals(false, $object->bar(4));
	}
}

Use meaningful names for test methods[edit]

Meaningful names for your test methods increase readability and add a bit of extra documentation to your test. Meaningful tests also give a really nice output when using the --testdox option of PHPUnit (see below).

Using the example above, meaningful method names could look like this:

<?php
class FooTest extends PHPUnit_Framework_Testcase
{
	public function testStringIsConvertedToUppercase()
	{
		$object = new foo();

		$this->assertEquals('EXAMPLE', $object->bar('example'));
	}

	public function testFalseIsReturnedWhenIntIsUsedAsArgument()
	{
		$object = new foo();

		$this->assertEquals(false, $object->bar(4));
	}
}

Use the most specific assertion possible[edit]

PHPUnit offers a wide range of different assertion methods. By using the most specific one that's available, you reduce code in your tests and also make your tests more strict which leads to more meaningful results. Again, let's improve the example used above:

<?php
class FooTest extends PHPUnit_Framework_Testcase
{
	public function testStringIsConvertedToUppercase()
	{
		$object = new foo();

		// assertSame is type safe, so in this case only strings are accepted
		$this->assertSame('EXAMPLE', $object->bar('example'));
	}

	public function testFalseIsReturnedWhenIntIsUsedAsArgument()
	{
		$object = new foo();

		// reduced code
		$this->assertFalse($object->bar(4));
	}
}

Run your tests with --strict and --verbose[edit]

By running your tests with the --strict and --verbose parameters, PHPUnit will provide you a lot of useful informations, for example:

  • Tests that don't make any assertions and therefor are useless
  • Tests that have a todo
  • Tests that are skipped

Generating Testdox[edit]

When test methods have meaningful names, the --testdox parameter is a very useful instrument to get a human readble, nicely looking overview of passing and falling tests. Running our example from above with --testdox outputs:

PHPUnit 4.3.1 by Sebastian Bergmann.

Foo
 [x] String is converted to uppercase
 [x] False is returned when int is used as argument

To generate this output, PHPUnit uses the method name, strips the "test" at beginning and converts each uppercase letter into a space.