"Test Injection" design pattern
Environment dependencies
Consider the example, your application is required a user to be logged in before doing any manipulations with data. You have implemented a password validator, for example:
<?php
class Model_User
{
public function isValidPassword($password)
{
return $this->getPassword() == $password;
}
}
When developing this application in your local environment you should create a new user in the database and setup a password. Imagine the situation when method getPassword() is time-consuming and depends on, say, LDAP. You don't have access to production LDAP server and you need to put a temporary stub into your validator:
<?php
class Model_User
{
public function isValidPassword($password)
{
// user will be able to login with any password
// in testing and development environments
if (APPLICATION_ENV != 'production')
return true;
return $this->getPassword() == $password;
}
}
When application is quite big and depends on many third-party resources around it, there will be many such environment dependent conditions inside the code. And it will become more and more difficult to manage them.
"Test Injection", the idea
There is another approach, which we can call "test injection". You move all environment dependencies outside your code and inject them right the same way you inject dependencies. You should create a separate class, instantiated and executed in your bootstrap:
<?php
class Bootstrap extends FaZend_Application_Bootstrap_Bootstrap
{
protected function _initTestInjector()
{
require_once 'test/injector/Injector.php';
$injector = new Injector();
$injector->inject();
}
}
This class will configure all your other classes, by means of injecting specific data and behavior:
<?php
class Injector extends FaZend_Test_Injector
{
protected function _injectLogin()
{
// don't validate user passwords
Model_User::setPasswordIsValidated(false);
}
}
Your user password validator now will look differently, more clear and environment independent:
<?php
class Model_User
{
protected static $_passwordIsValidated = true;
public static function setPasswordIsValidated($is = true)
{
self::$_passwordIsValidated = $is;
}
public function isValidPassword($password)
{
if (!self::$_passwordIsValidated)
return true;
return $this->getPassword() == $password;
}
}
Now all your environment dependencies are located in one single class Injector and you can control behavior of your application in one place. Instead of spreading if-conditions through the whole project.
Moreover, Injector class will be executed before any unit test (since it's called in your bootstrap) and will become a "global" setUp() method.
Copyright Notice: The article is published by FaZend.com and is protected by US and International copyright laws. You may not republish, copy, reproduce or distribute this article or its paragraphs or elements. You may reference the article in your documentation with a mandatory notice about the authorship of the material. If you have any other privacy concerns about the materials published on FaZend.com website you shall email to privacy@fazend.com.
