I have a class like:
class Foo {
function getCurrentBar() {
$model = Query::findByPk($this->getSession()->get('current_bar')); // Pseudocode...
return $model;
}
}
So basically there's one bar at a time in my application and it is stored in the session as an integer. I frequently call a helper function which will find the model instance, it makes a db query. There is caching as well, but that won't matter now.
My question is this: how do I unit test with this? There are classes that I test that need this. I guess I could alter the session to contain the id but then it means I need to have a corresponding model in the database.
Would the best approach be to add a method setCurrentBar() just for unit testing purposes? Then I could mock a bar object and set it and it will be used for all unit tests. Does that make sense?
The answer is indeed dependency injection, but the dependency you want to control is the source of bars, not the bar itself.
class Foo {
private $query;
function __construct($query)
{
$this->query = $query;
}
function getCurrentBar() {
$model = $this->query->findByPk($this->getSession()->get('current_bar')); // Pseudocode...
return $model;
}
}
So in your production code you have
$query = new Query() // assuming findByPk() is made a normal non-static method
$realFoo = new Foo($query);
but for unit testing...
$testFoo = new Foo(new MockQuery());
where MockQuery is a mock version of your Query class which returns mock bars.
You could try using dependency injection.
Pass the Bar instance to the Foo constructor
class Foo
{
private _bar;
function __construct (Bar $bar)
{
$this->_bar = $bar;
}
public function getCurrentBar()
{
return $this->_bar;
}
}
Then you can inject your mock Bar Object when you run your test.
You could also choose to test a partial mock object, as an interim until a DI system is in place.
class Foo {
function getCurrentBar() {
$model = $this->getQueryResult($this->getSession()->get('current_bar')); // Pseudocode...
return $model;
}
function getQueryResult($queryParams)
{
return Query::findByPk($queryParams);
}
}
Then do a test that looks a bit like this...
$myObjectToTest = $this->getMock('Foo', array('getQueryResult'), array(), '', true, true, true);
$myObjectToTest->expects($this->once())
->method('getQueryResult')
->with($whateverTheQueryParametersAre)
->will($this->returnValue($someResult));
$myObjectToTest->getCurrentBar();
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.
Basically I have a method which I need to run when the constructor is finished (the method is called persist() and it simply saves a key which was generated during the constructor into the session). It seems simple enough, and it works - at the end of __construct I make the call to $this->persist().
The problem is that this class is subclassed many times. This causes two issues.
One, that I must remember to make the call to persist() at the end of every single subclass's __construct method. Not a huge issue but it doesn't feel very OOP, I feel like I could be dealing with this in the parent class some how and that this would be better.
Two, if a subclass is subclassed (which it is), and the __construct methods chained (i.e. parent::__construct called), the persist() method will be getting fired multiple times, once for each time the class has been subclassed. It only needs to be called once, when all construction is complete. In this scenario it doesn't really break anything because when the persist method is called for the 2nd, 3rd time etc., it simply overwrites what was persisted before. But that isn't the point, because I just feel like there must be a better way and that there are scenarios out there that would not allow for the method to be called multiple times.
Is a factory method which constructs the object and then makes the call to persist on it the only way? I can go down this route but I am just wondering if there is a way to do it without, so that the method from the parent is always called after construction.
Here is some example code:
session_start();
is(!isset($_SESSION["Component"])) $_SESSION["Component"] = [];
abstract Class Component
{
private $id;
protected $key;
function __construct($id = NULL)
{
$this->id = $id;
$this->key = [];
$this->key["something"] = "SomeValue";
$this->persist(); // First call
}
protected function persist()
{
if($this->id !== NULL) $_SESSION["Component"][$this->id] = $this->key;
}
}
Class SomeComponent extends Component
{
function __construct($id = NULL)
{
parent::__construct($id);
$this->key["something-else"] = "SomeOtherValue";
$this->persist(); // Second call
}
}
Class SomeSpecialistComponent extends SomeComponent
{
function __construct($id = NULL, $key = [])
{
parent::__construct($id);
$this->key = array_merge($this->key, $key);
$this->persist(); // Third call
}
}
$my_component = new SomeSpecialistComponent(1, ["example" => true]);
Only trick I found to get something similar (except I wanted to execute things before and not after) is using a parent class with an abstract method as a new constructor :
abstract class RequireThings {
public function __construct() {
$this->constructAndPersist();
$this->persist();
}
abstract function constructAndPersist();
// You could also set this function in your children classes by the way.
public function persist() {
echo ' Then I persist!';
}
}
class UsingPersist extends RequireThings {
public function constructAndPersist() {
echo 'I do my things first.';
}
}
$class = new UsingPersist();
Would output :
I do my things first. Then I persist!
If I got your problem right, it should be enough to avoid problems you are facing.
The main downside of this solution is that you have to use a new function which is supposed to be your new constructor for this type of classes. That's why I set the __constructPersist as abstract, it forces the behavior as wanted.
I would argue in favor of the factory method, mostly because you're doing real work in the constructor. Remove the call where work is being done in the constructors ($this->persist) and place it in the factory:
class ComponentFactory
{
const SOME_COMPONENT = 'component';
const SOME_SPECIALIST_COMPONENT = 'specialist_component';
public static function make($type, $id, $key = null)
{
switch($type) {
case self::SOME_COMPONENT:
$component = new SomeComponent($id);
break;
case self::SOME_SPECIALIST_COMPONENT:
$component = new SomeSpecialistComponent($id, $key);
break;
}
$component->persist();
return $component;
}
}
$component = ComponentFactory::make(ComponentFactory::SOME_COMPONENT, 42);
$specialist = ComponentFactory::make(
ComponentFactory::SOME_SPECIALIST_COMPONENT,
43,
[
'something' => 'SomeValue',
'something-else' => 'SomeOtherValue',
]
);
According to Miško Hevery (author of AngularJS and agile coach at Google) these are the warning signs of doing too much work in the constructor:
new keyword in a constructor or at field declaration
Static method calls in a constructor or at field declaration
Anything more than field assignment in constructors
Object not fully initialized after the constructor finishes (watch
out for initialize methods)
Control flow (conditional or looping logic) in a constructor
CL does complex object graph construction inside a constructor
rather than using a factory or builder
Adding or using an initialization block
just create another function that you'll call before $this->persist and override that in your subclasses instead of the constructor
Here is the class I am unit testing. Currently I am testing the doSomething function:
class FooClass {
public function doSomething( $user ) {
$conn = $this->getUniqueConnection( $user->id );
$conn->doSomethingDestructive();
}
private function getUniqueConnection( $id ) {
return new UniqueConnection( $id );
}
}
As you can see, the doSomething function gets a new instance of UniqueConnection (a class I am not testing here) based on a property of the argument it receives. The problem is that UniqueConnection:: doSomethingDestructive method is something I cannot call during tests due to its... destructiveness. So I would like to stub/mock the UniqueConnection rather than use a real one.
I don't see any way to inject my mocked UniqueConnection. I would make the UniqueConnection a constructor argument for FooClass but, as you can see, a new one gets created based on the parameter to the doSomething function and all the unique ids it may be called with are not known ahead of time.
My only option that I can see is to test a mock of FooClass instead of FooClass itself. Then I would replace the getUniqueConnection function with one that returns a mock/stub. This seems bad to test an mock, but I don't see any way to achieve what I am after otherwise. UniqueConnection is a third party vendor library and cannot be modified.
You could make a UniqueConnectionFactory, and pass an instance of that to FooClass. Then you have
private function getUniqueConnection( $id ) {
return $this->uniqueConnectionFactory->create( $id );
}
In general, this is one of the benefits of using a factory - you keep the new operator out of the class, which allows you to more easily vary the object being created.
Like Rambo Coder said, it's a matter of doing too much in your class. I wouldn't go as far as wanting to create a Factory, especially if you'll only ever create an instance of one specific class. The simplest solution would be to invert the responsibility of creating the UniqueConnection:
<?php
class FooClass {
public function doSomething( UniqueConnection $connection ) {
$connection->doSomethingDestructive( );
}
}
Pass a mock when you're testing, pass a new UniqueConnection( $user->id ) in the real code..
Until you can take the time to refactor the code to use a factory as rambo coder recommends, you can use a partial mock to return a non-destructive unique connection. When you find yourself in this position, it usually means the class under test has more than one responsibility.
function testSomething() {
$mockConn = $this->getMock('UniqueConnection');
$mockConn->expects($this->once())
->method('doSomethingDestructive')
->will(...);
$mockFoo = $this->getMock('FooClass', array('getUniqueConnection'));
$mockFoo->expects($this->once())
->method('getUniqueConnection')
->will($this->returnValue($mockConn));
$mockFoo->doSomething();
}
Creating classes in a way that it can support different modes of execution is very important in some cases. One of these cases is what you are asking for.
Create your classes to support various modes. For example
Class Connection {
private $mode;
public function setMode($mode) {
$this -> $mode = $mode;
}
}
Now, your doSomethingDestructive can act as per the execution mode.
public function doSomethingDestructive() {
if($this -> mode === "test") { //if we are in a test mode scenario
//Log something
// Or just do some logging and give a message
} else {
// do what it was suppose to do
}
}
Next time, when you are testing the class, you dont have to worry about that destructive function doing something destruction accidentally.
public function doSomething( $user ) {
$conn = $this->getUniqueConnection( $user->id );
$conn -> setMode("test"); //Now we are safe
$conn->doSomethingDestructive(); //But the Testing is still being Ran
}
In this case what you want is not a mock object, but a testing subclass. Break your $conn->doSomethingDestructive(); into a method, then subclass FooClass as TestFooClass and override the new method in the subclass. Then you can test using the subclass without getting the unwanted destructive behavior.
For example:
class FooClass {
public function doSomething( $user ) {
$conn = $this->getUniqueConnection( $user->id );
$this->connDoSomethingDestructive($conn);
}
protected function connDoSomethingDestructive($conn) {
$conn->doSomethingDestructive();
}
private function getUniqueConnection( $id ) {
return new UniqueConnection( $id );
}
}
class TestFooClass extends FooClass {
protected function connDoSomethingDestructive() {
}
private function getUniqueConnection( $id ) {
return new MockUniqueConnection( $id );
}
}
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.
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.