Help:Using mock objects

From Linux Web Expert

Writing tests requires diligence and careful orchestration in order for a test to be meaningful both in a way it handles input and how it asserts expected output.

In most cases where a class is covered by a test, the class should be treated as unit (hence the name "unit test") and should try to eliminate any uncontrollable influence( use of $GLOBALS etc. ) that is not directly handled by the class "under test". Achieving such separation is difficult without relying on outside (objects that under no control of the class being tested) dependencies.

In order to control such dependencies the use of Fake- or MockObjects is advisable as enables part of the functionality to be simulated (create canned behaviour) that would be expected in a production environment. Another advantage of a mock object is the ability to alter conditions (being a normal Title, being a Title marked as SpecialPage etc.) so that dependent objects can be monitored on whether to produce expected results or not.

MockObjectBuilder

Semantic MediaWiki makes it a bit easier to create readable mock objects by using the MockObjectBuilder while object definitions are kept in the MockObjectRepositoryclass.

$mockObjectBuilder = new SMW\Test\MockObjectBuilder();

For example, if a test would need to create a Title mock object it would need to create the following in each test that where rely on a mocked Title.

$mockTitle = $this->getMockBuilder( 'Title' )
	->disableOriginalConstructor()
	->getMock();

$mockTitle->expects( $this->any() )
	->method( 'isSpecialPage' )
	->will( $this->returnValue(  true ) );
...

Fortunately, Semantic MediaWiki provides a short cut for most common used mock objects within its test environment.

$this->newMockBuilder()->newObject( 'aConcreteObject', array( ... ) );
$mockTitle = $this->newMockBuilder()->newObject( 'Title', array(
	'isSpecialPage' => true
) );

Example

/**
 * @dataProvider titleDataProvider
 * @since 1.9
 */
public function testTitleInstanceOnMock( $title, $message ) {
	$this->assertInstanceOf( 'Title', $title, $message );
}

/**
 * @return array
 */
public function titleDataProvider() {

	$provider = array();

	$provider[] = array(
		$this->newMockBuilder()->newObject( 'Title', array( 'isSpecialPage' => false ) ),
		'Asserts that Title is a not a special page'
	);

	$provider[] = array(
		$this->newMockBuilder()->newObject( 'Title', array( 'isSpecialPage' => true ) ),
		'Asserts that Title is a special page'
	);

	return $provider;
}

Callbacks

For even greater flexibility, a callback can be invoked to manipulate dependencies where more individual fine tuning is required by the test.

$this->newMockBuilder()->newObject( 'Store', array(
	'getPropertyValues' => array( $this, 'mockStorePropertyValuesCallback' ),
) );

Of course, sometimes it is unavoidable to use the standard getMockBuilder() instead because the object that needs to be mocked requires a special setup that would be beyond the scope of the MockObjectBuilder.

See also

[a.1] http://net.tutsplus.com/tutorials/php/all-about-mocking-with-phpunit/

[a.3] http://codeutopia.net/blog/2009/06/26/unit-testing-4-mock-objects-and-testing-code-which-uses-the-database/

[a.3] http://phpunit.de/manual/3.7/en/test-doubles.html