I am looking to incorporate a testing framework into a project I am building and came across Enhance PHP which I like but I am having some difficulty finding relevant information on-line since "enhance php" is such a commonly used phrase.
Has anyone worked with this framework that might be able to point me toward some helpful guide? Have you worked with a unit test framework that you think is amazingly better?
Thanks in advance.
In response to Gotzofter, this is the class to be tested:
<?php
include_once('EnhanceTestFramework.php');
class ExampleClass
{
private $OtherClass;
function __construct($mock = null)
{
if ($mock == null)
$this->OtherClass = new OtherExampleClass();
else
$this->OtherClass = $mock;
}
public function doSomething()
{
return $this->OtherClass->getSomething(1, 'Arg2');
}
}
class OtherExampleClass
{
public function getSomething()
{
return "Something";
}
}
class ExampleClassTests extends \Enhance\TestFixture
{
public function setUp()
{
}
public function tearDown()
{
}
public function verifyWithAMock()
{
$mock = \Enhance\MockFactory::createMock('OtherExampleClass');
$mock->addExpectation(
\Enhance\Expect::method('getSomething')
->with(1, 'Arg2')
->returns('Something')
->times(1)
);
$target = new ExampleClass($mock);
$result = $target->doSomething();
\Enhance\Assert::areIdentical("Something", $result);
$mock->verifyExpectations();
}
}
\Enhance\Core::runTests();
look at my constructor for ExampleClass.
Because enhance-php's site example injects the $mock object by calling new ExampleClass($mock), I am forced to change my ExampleClass constructor to handle a $mock as an input parameter.
Do I have to handle this for all classes that I want to subject to unit testing with the framework?
Thanks.
This:
function __construct()
{
$this->OtherClass = new OtherExampleClass;
}
Should be:
function __construct($otherClass)
{
$this->OtherClass = $otherClass;
}
Your mock is never injected at this point in your test:
$target = new ExampleClass($mock);
One thing I would recommend no matter what testing framework you are using is type-hinting against the expected class, or interface.
<?php
class ExampleClass
{
private $OtherClass; // OtherClass instance
public function __construct(OtherClass $OtherClass=null)
{
// ...
}
}
I'm no di expert, but I don't see the problem in letting each class call new if an instance isn't provided for a particular dependency. You could also of course take the approach where you use setter methods to configure dependencies.
<?php
class class ExampleClass
{
private $OtherClass; // OtherClass instance
public function setOtherClass(OtherClass $OtherClass)
{
$this->OtherClass = $OtherClass;
}
}
It is lame that the ExampleClass in the sample code doesn't even define the doSomething method from the ExampleDependencyClassTests, but if I understand correctly it looks like Enhance PHP is not forcing you to take a particular style of dependency injection. You can write the test class however you want, so for example if you took the setter method approach I mentioned above, you could change the example mock code to
<?php
class ExampleDependencyClassTests extends \Enhance\TestFixture
{
public function verifyWithAMock()
{
$mock = \Enhance\MockFactory::createMock('ExampleDependencyClass');
$mock->addExpectation(
\Enhance\Expect::method('getSomething')
->with(1, 'Arg2')
->returns('Something')
->times(1)
);
$target = new ExampleClass();
$target->setExampleDependencyClass($mock);
$result = $target->doSomething();
$mock->verifyExpectations();
}
}
Of course it would probly make sense to make the appropriate revisions to the ExampleClass!
<?php
class ExampleClass
{
private $ExampleDependencyClass;
public function addTwoNumbers($a, $b)
{
return $a + $b;
}
public function setExampleDependencyClass(
ExampleDependencyClass $ExampleDependecyClass
) {
$this->ExampleDependecyClass = $ExampleDependecyClass;
}
public function doSomething($someArg)
{
return 'Something';
}
}
I've worked with PHPUnit quite a bit, and honestly you'll have to face the same challenges with Mocks there. My 2 cents, try to model your tests without Mocks if possible ;)
There is a tutorial on NetTuts titled Testing Your PHP Codebase With Enhance PHP, which will definitely help you to get started.
And there is a Quick Start Guide on Enhance PHP.
Related
I have the following code (simplified and details changed for this question):
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// already doing constructor injection for the data object
public __construct($data_object) {
// details here
}
public function add_new_thing_A($has_relationship) {
$thing_A = new Thing_A();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = new Thing_B();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
In the above example, I have to decouple the instantiation of Thing_A and Thing_B from the add_new_thing method. However, a simple constructor injection will not do for these two classes. This is because I need fresh instances of Thing_A and Thing_B every time add_new_thing is called so that Thing_A can be added to the array_of_thing_A.
How can I make this function unit testable? And more specifically for me to use mocks of Thing_A and Thing_B in testing this function in PHPUnit?
Any suggestions with code example will be appreciated.
Additionally, I would like to mention that Thing_A and Thing_B are used elsewhere in the codebase that I am working with and the code using these classes will eventually need to be unit tested. Solutions that are too localized and would cause repeated code elsewhere will not be too ideal in my situation. Thank you.
As commenter xmike mentioned, you could use the factory pattern. You would inject a factory object through the ctor as well. Then you could have a factory that provides simplified instances of your Thing_A and Thing_B.
class ThingFactory {
public function buildThingA() {
return new Thing_A(); // or MockThing_A if you go the ducktyping route
}
public function buildThingB() {
return new Thing_B();
}
}
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// you could go the typed route and have an interface for this
private $factory;
// already doing constructor injection for the data object
public __construct($data_object, $factory) {
// details here
$this->factory = $factory;
}
public function add_new_thing_A($has_relationship) {
$thing_A = $this->factory->buildThingA();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = $this->factory->buildThingB();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
PHP is such a strange language, you can't assign a class to a variable. But you can do it as a string. Inject ThingA and ThingB on the constructor as strings. You can call new on the string member.
class ThingA {};
class ThingB{};
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
private $_thingA;
private $_thingB;
public function __construct($data_object, $thingA, $thingB) {
$this->_thingA = $thingA;
$this->_thingB = $thingB;
}
public function add_new_thing_A($has_relationship) {
$thing_A = new $this->_thingA();
if ($has_relationship) {
$thing_B = new $this->_thingB();
}
$this->array_of_thing_A[] = $thing_A;
}
}
$model = new model_to_be_tested('foo', 'ThingA', 'ThingB');
$model->add_new_thing_A(true);
There's a live version here: https://repl.it/#rmoskal/InconsequentialAnotherGermanshorthairedpointer
Or provide a static constructor for the class.
Likely this has already been asked, but nevertheless, here goes. This may fall under best practice or security... I'm not really sure.
In my application, I am using a nested object, that is called in the __construct() function. Sort of like this:
class user {
public $userID = NULL;
public $someObject = NULL;
public function __construct() {
$this->userID = getThisUser();
$this->someObject = new objectBuilder($this->userID);
}
public function getThisUser() {
// ...
}
}
class objectBuilder {
public $buriedVar = NULL;
public function __construct($uid = NULL) {
if( !isset($uid) ) {
$this->buriedVar = setTheObject($uid);
} else {
$this->buriedVar = setTheObject(0);
}
}
public function setTheObject($id) {
// ...
return "random string";
}
}
$tom = new user();
Obviously terrible outline here, but the point is, I can then call $tom->someObject->buriedVar and it'll return "random string".
While looking for a way to nest classes, I noticed no one recommends this as a method for storing objects inside of another object. I'm curious of a few things:
1) Is this insecure?
2) Are the vars inside the nested object exclusive to the call made inside $tom->__construct(), or if I create another object using new objectBuilder() is it overwriting the one inside $tom->someObject? I haven't noticed this, but am not sure how to test for that entirely.
3) Is there something else I'm missing? A best practice reason not to instantiate an object inside a class? I've been using it for years and it works great for what I've done. Is it a speed thing?
1) Is this insecure?
Not inherently, no.
2) Are the vars inside the nested object exclusive to the call made
inside $tom->__construct(), or if I create another object using new
objectBuilder() is it overwriting the one inside $tom->someObject? I
haven't noticed this, but am not sure how to test for that entirely.
This is a fundamental question between class and object. Objects are instances of a class and there can be multiple. The only things that would be overwritten are static properties and methods. You could test it like this:
<?php
$obj1 = new objectBuilder();
$obj2 = new objectBuilder();
if ($obj1 !== $obj2) {
echo "objects are not the same\n";
}
if ($obj1->buriedVar !== $obj2->buriedVar) {
echo "nested objects are not the same either\n";
}
$obj3 = new objectBuilder(1);
if ($obj1->buriedVar != $obj3->buriedVar) {
echo "even the values of two different buried vars with different values are different.\n";
}
if ($obj1->buriedVar == $obj2->buriedVar) {
echo "counter-example: nested variables with the same values set are similar.\n";
}
It helps to know the difference between equality and identity (see this SO post).
3) Is there something else I'm missing? A best practice reason not to
instantiate an object inside a class? I've been using it for years and
it works great for what I've done. Is it a speed thing?
You touched on it briefly. What you should know is that this is not scalable and is difficult to test.
Imagine you're creating a website for dogs.
<?php
class Bio
{
public function __construct()
{
$this->dog = new Dog('Terrier');
}
}
class Dog
{
private $animal = 'dog';
private $noise = 'woof!';
private $breed;
public function __construct($breed=null)
{
$this->setBreed($breed);
}
public function setBreed($breed)
{
$this->breed = $breed;
}
}
What if you want to add a new breed? Well... That's easy enough:
class Bio
{
// ...
public function __construct($breed)
{
$this->dog = new Dog($breed);
}
// ...
}
Cool! You've solved everything.
Except...
One day you want to create a section for cats, because one of your best writers also loves cats, and you sense an untapped market.
Uh oh...
You can refactor the code, of course. But you wrote it a long time ago. Now you have to go in and figure out where everything went. No big deal.. A bit annoying but you fixed it!
But now you have another problem. Turns out that the same author wants to add different traits to the breed. You're surprised this hasn't come up sooner but, hey, it's probably a good thing to have.
Now you need to go in to the Dog object, and the Cat object, and add traits.
Every single time.
On. Every. Bio.
After some reconfiguring, you've created something monstrous like this:
$article1 = new Bio('Terrier', 'dog', ['independent']);
$article2 = new Bio('Persian', 'cat', ['flat-faced']);
//... and so on, and so on
The next time the author asks for something, you fire her and then tear your hair out in a mad rage.
Or, from the beginning, you use Dependency Injection.
<?php
class Bio
{
private $animal;
public function __construct(AnimalInterface $animal)
{
$this->animal = $animal;
}
}
interface Animal
{
public function getType();
public function setBreed($breed);
public function getBreed();
public function setTraits(array $traits);
public function getTraits();
}
abstract class AbstractAnimal implements AnimalInterface
{
private $breed;
private $traits = [];
abstract public function getType();
public function setBreed($breed)
{
$this->breed = $breed;
}
public function getBreed()
{
return $this->breed;
}
public function setTraits(array $traits)
{
$this->traits = $traits;
}
public function getTraits()
{
return (array)$this->traits;
}
}
class Cat extends AbstractAnimal
{
public function getType()
{
return 'cat';
}
}
class Dog extends AbstractAnimal
{
public function getType()
{
return 'dog';
}
}
This pattern requires little to no editing after it has been created.
Why? Because you are injecting the object to nest into the class, rather than instantiating it in the object.
$bio1 = new Bio($dog); $bio2 = new Bio($cat); can always stay like this. Now you just edit the $dog and $cat objects. The added benefit is that these objects can be used anywhere.
But what about utility classes?
(This is where testability comes in. If you haven't worked with unit testing, I recommend reading up on it in the link to PHPUnit below. I'm not going to dwell on how that works as it's off topic).
Dependency Injection is well and good if you have classes that require customization. But what about utility classes that just house various functions?
class Utils
{
public function add($a, $b)
{
return $a + $b;
}
}
You might think that you can call this function safely from the constructor. And you can. However, one day you might create a log method in your Utils class:
public function log($msg)
{
exec("cat '$msg' > /tmp/log.txt");
}
This works just fine. However, when you run tests, your /tmp/log.txt file complains. "Invalid permissions!". When this method is run via your website, log.txt needs to be writeable by www-data.
You could just chmod 777 /tmp/log.txt, but that would mean everyone who has access to your server can write to that log. Additionally, you may not want to always write to the same log when you're testing as when you're navigating through the web interface (Personally, I would find it confusing and cluttering).
PHPUnit and other unit testing services allow you to mock various objects. The problem is that you have classes calling Utils directly.
You have to find a way to manually override the constructor. Look at PHPUnit's manual to find out why this maybe isn't ideal.
So if you're not using Dependency Injection, what do you do?
PHPUnit suggests, amongst other fixes, moving this Utils object instantiation to another method and then stubbing/mocking that method in your unit test (I want to emphasize that this is after recommending Dependency Injection).
So the next best?
public function __construct()
{
$this->init();
}
private function init()
{
$this->utils = new Utils;
}
Now when you unit test, you can create a fake init method and it will be called as soon as the class is created.
In conclusion, the way you are currently instantiating classes is not scalable or easily testable in many real world situations. While it may be all right in limited situations, it is better to get used to the DI (Dependency Injection) pattern, because it will save you lots of headaches in the future.
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
I'm currently writing a small console application on the Symfony 2 framework. I'm attempting to insulate the application from the framework (mainly as an exercise after hearing some interesting talks on hexagonal architecture/ports and adaptors, clean code and decoupling applications from frameworks), so that it could potentially be run as a console application, a web application, or moved to another framework with little effort.
The issue I'm having is when one of my interfaces is implemented using the adaptor pattern and it depends on another interface that is also implemented using the adaptor pattern. It's difficult to describe and is probably best described with a code example. Here I've prefixed my class/interface names with "My", just to make it clear which code is my own (and I can edit) and which belongs to the Symfony framework.
// My code.
interface MyOutputInterface
{
public function writeln($message);
}
class MySymfonyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
}
interface MyDialogInterface
{
public function askConfirmation(MyOutputInterface $output, $message);
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
$this->dialog->askConfirmation($output, $message); // Fails: Expects $output to be instance of \Symfony\Component\Console\Output\OutputInterface
}
}
// Symfony code.
namespace Symfony\Component\Console\Helper;
class DialogHelper
{
public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
{
// ...
}
}
One extra thing to note is that \Symfony\Component\Console\Output\ConsoleOutput implements \Symfony\Component\Console\Output\OutputInterface .
To conform to MyDialogInterface, the MySymfonyDialogAdaptor::askConfirmation method must take an instance of MyOutputInterface as an argument. However, the call to Symfony's DialogHelper::askConfirmation method expects an instance of \Symfony\Component\Console\Output\OutputInterface, meaning the code won't run.
I can see a couple of ways around this, neither of which are particularly satisfactory:
Have MySymfonyOutputAdaptor implement both MyOutputInterface and Symfony\Component\Console\Output\OutputInterface. This isn't ideal, as I'd need to specify all of the methods in that interface, when my application only really cares about the writeln method.
Have MySymfonyDialogAdaptor assume that the object passed to it is an instance of MySymfonyOutputAdaptor: If it's not, then throw an exception. Then add a method to the MySymfonyOutputAdaptor class to obtain the underlying \Symfony\Component\Console\Output\ConsoleOutput object, which can be passed to Symfony's DialogHelper::askConfirmation method directly (as it implements Symfony's OutputInterface). This would look something like the following:
class MySymfonyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
public function getSymfonyConsoleOutput()
{
return $this->output;
}
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
if (!$output instanceof MySymfonyOutputAdaptor) {
throw new InvalidArgumentException();
}
$symfonyConsoleOutput = $output->getSymfonyConsoleOutput();
$this->dialog->askConfirmation($symfonyConsoleOutput, $message);
}
}
This feels wrong: If MySymfonyDialogAdaptor::askConfirmation has a requirement that its first argument is an instance of MySymfonyOutputAdaptor, it should specify it as its typehint, but that would mean it no longer implements MyDialogInterface. Also, accessing the underlying ConsoleOutput object outside of its own adaptor doesn't seem ideal, as it should really be wrapped by the adaptor.
Can anyone suggest a way around this? I feel like I'm missing something: Perhaps I'm putting adaptors in the wrong places and rather than multiple adaptors, I just need one adaptor wrapping the whole output/dialog system? Or maybe there's another inheritance layer I need to include in order to implement both interfaces?
Any advice appreciated.
EDIT: This issue is very similar to the one described in the following pull-request: https://github.com/SimpleBus/CommandBus/pull/2
After much discussion with colleagues (thanks Ian and Owen), plus some help from Matthias via https://github.com/SimpleBus/CommandBus/pull/2 , we've come up with the following solution:
<?php
// My code.
interface MyOutputInterface
{
public function writeln($message);
}
class SymfonyOutputToMyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
}
class MyOutputToSymfonyOutputAdapter implements Symfony\Component\Console\Output\OutputInterface
{
private $myOutput;
public function __construct(MyOutputInterface $myOutput)
{
$this->myOutput = $myOutput;
}
public function writeln($message)
{
$this->myOutput->writeln($message);
}
// Implement all methods defined in Symfony's OutputInterface.
}
interface MyDialogInterface
{
public function askConfirmation(MyOutputInterface $output, $message);
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
$symfonyOutput = new MyOutputToSymfonyOutputAdapter($output);
$this->dialog->askConfirmation($symfonyOutput, $message);
}
}
// Symfony code.
namespace Symfony\Component\Console\Helper;
class DialogHelper
{
public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
{
// ...
}
}
I think the concept I was missing was that adaptors are essentially one-directional (e.g. from my code to Symfony's, or vice versa) and that I needed another separate adaptor to convert from MyOutputInterface back to Symfony's OutputInterface class.
This isn't completely ideal, as I still have to implement all of Symfony's methods in this new adaptor (MyOutputToSymfonyOutputAdapter), but this architecture does feel quite well-structured, as it is clear that each adaptor converts in one direction: I've renamed the adaptors accordingly to make this more clear.
Another alternative would be to fully implement only the methods that I wanted to support (just writeln in this example) and define the other methods to throw an exception to indicate they are unsupported by the adaptor if they are called.
Many thanks for the help all.
I'm used to the habit of writing like this:
$results = SomeModelQuery::create()->filterByFoo('bar')->find();
However this does not scale for unit testing because I can't inject a mock object, i.e. I can't affect what data is returned. I'd like to use fixture data, but I can't.
Nor does it seem great to inject an object:
class Foo
{
public __construct($someModelQuery)
{
$this->someModelQuery = $someMOdelQuery;
}
public function doSthing()
{
$results = $this->someModelQuery->filterByFoo('bar')->find();
}
}
DI feels horrible. I have tens of query objects to mock and throw. Setting through constructor is ugly and painful. Setting using method is wrong because it can be forgotten when calling. And it feels painful to always for every single lib and action to create these query objects manually.
How would I elegantly do DI with PropelORM query classes? I don't want to call a method like:
$oneQuery = OneQuery::create();
$anotherQuery = AnotherQuery::create();
// ... 10 more ...
$foo = new Foo($oneQuery, $anotherQuery, ...);
$foo->callSomeFunctionThatNeedsThose();
In my opinion (and Martin Folowers's) there is a step between calling everything statically and using Dependency Injection and it may be what you are looking for.
Where I can't do full DI (Zend Framework MVC for example) I will use a Service Locator. A Service Layer will be the place that all your classes go to get there dependencies from. Think of it as a one layer deep abstraction for your classes dependencies. There are many benefits to using a Service Locator but I will focus on testability in this case.
Let's get into some code, here is are model query class
class SomeModelQuery
{
public function __call($method, $params)
{
if ($method == 'find') {
return 'Real Data';
}
return $this;
}
}
All it does is return itself unless the method 'find' is called. Then is will return the hard-coded string "Real Data".
Now our service locator:
class ServiceLocator
{
protected static $instance;
protected $someModelQuery;
public static function resetInstance()
{
static::$instance = null;
}
public static function instance()
{
if (self::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getSomeModelQuery()
{
if ($this->someModelQuery === null) {
$this->someModelQuery = new SomeModelQuery();
}
return $this->someModelQuery;
}
public function setSomeModelQuery($someModelQuery)
{
$this->someModelQuery = $someModelQuery;
}
}
This does two things. Provides a global scope method instance so you can always get at it. Along with allowing it to be reset. Then providing get and set methods for the model query object. With lazy loading if it has not already been set.
Now the code that does the real work:
class Foo
{
public function doSomething()
{
return ServiceLocator::instance()
->getSomeModelQuery()->filterByFoo('bar')->find();
}
}
Foo calls the service locator, it then gets an instance of the query object from it and does the call it needs to on that query object.
So now we need to write some unit tests for all of this. Here it is:
class FooTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
ServiceLocator::resetInstance();
}
public function testNoMocking()
{
$foo = new Foo();
$this->assertEquals('Real Data', $foo->doSomething());
}
public function testWithMock()
{
// Create our mock with a random value
$rand = mt_rand();
$mock = $this->getMock('SomeModelQuery');
$mock->expects($this->any())
->method('__call')
->will($this->onConsecutiveCalls($mock, $rand));
// Place the mock in the service locator
ServiceLocator::instance()->setSomeModelQuery($mock);
// Do we get our random value back?
$foo = new Foo();
$this->assertEquals($rand, $foo->doSomething());
}
}
I've given an example where the real query code is called and where the query code is mocked.
So this gives you the ability to inject mocks with out needing to inject every dependency into the classes you want to unit test.
There are many ways to write the above code. Use it as a proof of concept and adapt it to your need.