PHPUnit Mock Objects and Static Methods - php

I am looking for the best way to go about testing the following static method (specifically using a Doctrine Model):
class Model_User extends Doctrine_Record
{
public static function create($userData)
{
$newUser = new self();
$newUser->fromArray($userData);
$newUser->save();
}
}
Ideally, I would use a mock object to ensure that fromArray (with the supplied user data) and save were called, but that's not possible as the method is static.
Any suggestions?

Sebastian Bergmann, the author of PHPUnit, recently had a blog post about Stubbing and Mocking Static Methods. With PHPUnit 3.5 and PHP 5.3 as well as consistent use of late static binding, you can do
$class::staticExpects($this->any())
->method('helper')
->will($this->returnValue('bar'));
Update: staticExpects is deprecated as of PHPUnit 3.8 and will be removed completely with later versions.

There is now the AspectMock library to help with this:
https://github.com/Codeception/AspectMock
$this->assertEquals('users', UserModel::tableName());
$userModel = test::double('UserModel', ['tableName' => 'my_users']);
$this->assertEquals('my_users', UserModel::tableName());
$userModel->verifyInvoked('tableName');

I would make a new class in the unit test namespace that extends the Model_User and test that. Here's an example:
Original class:
class Model_User extends Doctrine_Record
{
public static function create($userData)
{
$newUser = new self();
$newUser->fromArray($userData);
$newUser->save();
}
}
Mock Class to call in unit test(s):
use \Model_User
class Mock_Model_User extends Model_User
{
/** \PHPUnit\Framework\TestCase */
public static $test;
// This class inherits all the original classes functions.
// However, you can override the methods and use the $test property
// to perform some assertions.
}
In your unit test:
use Module_User;
use PHPUnit\Framework\TestCase;
class Model_UserTest extends TestCase
{
function testCanInitialize()
{
$userDataFixture = []; // Made an assumption user data would be an array.
$sut = new Mock_Model_User::create($userDataFixture); // calls the parent ::create method, so the real thing.
$sut::test = $this; // This is just here to show possibilities.
$this->assertInstanceOf(Model_User::class, $sut);
}
}

Found the working solution, would to share it despite the topic is old.
class_alias can substitute classes which are not autoloaded yet (works only if you use autoloading, not include/require files directly).
For example, our code:
class MyClass
{
public function someAction() {
StaticHelper::staticAction();
}
}
Our test:
class MyClassTest
{
public function __construct() {
// works only if StaticHelper is not autoloaded yet!
class_alias(StaticHelperMock::class, StaticHelper::class);
}
public function test_some_action() {
$sut = new MyClass();
$myClass->someAction();
}
}
Our mock:
class StaticHelperMock
{
public static function staticAction() {
// here implement the mock logic, e.g return some pre-defined value, etc
}
}
This simple solution doesn't need any special libs or extensions.

Mockery's Alias functionality can be used to mock public static methods
http://docs.mockery.io/en/latest/reference/creating_test_doubles.html#creating-test-doubles-aliasing

Another possible approach is with the Moka library:
$modelClass = Moka::mockClass('Model_User', [
'fromArray' => null,
'save' => null
]);
$modelClass::create('DATA');
$this->assertEquals(['DATA'], $modelClass::$moka->report('fromArray')[0]);
$this->assertEquals(1, sizeof($modelClass::$moka->report('save')));

One more approach:
class Experiment
{
public static function getVariant($userId, $experimentName)
{
$experiment = self::loadExperimentJson($experimentName):
return $userId % 10 > 5; // some sort of bucketing
}
protected static function loadExperimentJson($experimentName)
{
// ... do something
}
}
In my ExperimentTest.php
class ExperimentTest extends \Experiment
{
public static function loadExperimentJson($experimentName)
{
return "{
"name": "TestExperiment",
"variants": ["a", "b"],
... etc
}"
}
}
And then I would use it like so:
public function test_Experiment_getVariantForExperiment()
{
$variant = ExperimentTest::getVariant(123, 'blah');
$this->assertEquals($variant, 'a');
$variant = ExperimentTest::getVariant(124, 'blah');
$this->assertEquals($variant, 'b');
}

Testing static methods is generally considered as a bit hard (as you probably already noticed), especially before PHP 5.3.
Could you not modify your code to not use static a method ? I don't really see why you're using a static method here, in fact ; this could probably be re-written to some non-static code, could it not ?
For instance, could something like this not do the trick :
class Model_User extends Doctrine_Record
{
public function saveFromArray($userData)
{
$this->fromArray($userData);
$this->save();
}
}
Not sure what you'll be testing ; but, at least, no static method anymore...

Related

Mock an object created in the middle of a method

I know that creating an instance of a Class in the middle of a method it's a bad practice since it makes code hard to test.
But I can't refactor the code, so I need to find a way to mock an Object created with new in the middle of a method under test.
Used Frameworks: PHPUnit, Mockery, WP_Mock
Example: Here I need to mock the get_second_string() method from the instance of the class ExternalClass
Class MyClass {
function methodUnderTest($string) {
$objToMock = new ExternalClass();
$second_string = $objToMock->get_second_string();
$final_string = $string . $second_string;
return $final_string;
}
}
Class TestMyClass extends PHPUnit_Framework_TestCase {
public function setUp() {
}
public function tearDown() {
}
public function test_methodUnderTest() {
$externalObject = $this->getMockBuilder('ExternalClass')
->setMethods(['get_second_string'])
->getMock;
$externalObject->expects($this->once())
->method('get_second_string')
->willReturn(' two');
$testObj = new MyClass();
$this->assertEquals('one two', $testObj->methodUnderTest('one');
}
}
If you really have no opportunity to refactor the code or do some appropriate integration testing, you might want to take a look at https://github.com/php-test-helpers/php-test-helpers#intercepting-object-creation and https://github.com/krakjoe/uopz/tree/PHP5
Still I think the code you make would profit a lot more from refactoring than monkey patching.
Besides, the refactoring does not need to be very heavy. You might do at least this:
class MyClass
{
private $externalsFactory;
public function __construct($externalsFactory){
$this->externalsFactory = $externalsFactory;
}
public function methodUnderTest($str){
$external = $this->externalsFactory->make();
$second_string = $external->get_second_string();
$finalString = $str.$second_string;
return $finalString;
}
}
class ExternalsFactory
{
public function make(){
return new ExternalClass();
}
}
class ExternalClass
{
public function get_second_string(){
return 'some real stuff may be even from database or whatever else it could be';
}
}
class MyClassTest extends PHPUnit_Framework_TestCase
{
private $factoryMock;
private $myClass;
public function setUp(){
$this->factoryMock = $this->getMockBuilder('ExternalsFactory')
->getMock();
$this->myClass = new MyClass($this->factoryMock);
}
public function testMethodUnderTest(){
$extenalMock = $this->createMock('ExternalClass');
$extenalMock->method('get_second_string')
->willReturn('second');
$this->factoryMock->method('make')
->willReturn($extenalMock);
$this->assertSame('first-and-second', $this->myClass->methodUnderTest('first-and-'));
}
}
IMHO there is no way to do such a thing. You should pass the object as parameter to the method.
You can not mock entire object.
But with phpunit you can do something like this:
$f = $this->getMockBuilder(<your_class>)->disableOriginalConstructor()
->setMethods(array(
<mocked_method_1>, <mocked_method_2>
))->getMock();
This way, newly created object ommits constructor and you specify which method are going to behave normally and which you mock.
in testing you can specify what the method/s will return, like this:
$f->method(<mocked_method_1>)->willReturn(<dummy_data>);
using this, you will not test the mocked object in any way, but can test method which is creating the object..

Access functions of other classes in SlimPHP

I have to different classes in my Slim PHP framework, named OrderController & AddressController. I want to access some function of AddressController inside OrderController to reduce code redundancy.
But can't get a way to do it, I got how to do it in pure PHP setup, but how to do it in Slim PHP framework?
The PHP way to do this is as follows:
class A {
private $xxx;
public function __construct() {
$this->xxx = 'Hello';
}
public function getXXX() {
return $this->xxx;
}
}
class B {
private $a;
public function __construct(A $a) {
$this->a = $a;
}
function getXXXOfA() {
return $this->a->getXXX();
}
}
$a = new A();
$b = new B($a);
$b->getXXXOfA();
How to achieve this dependancy injection in Slim?
Slim PHP Framework
Note: I am using Slim PHP v3
2 solutions come into mind:
-1-
You could also try to have the common functionality in a separate Trait.
-2-
I won't do the
new SecondController($container)
inside the constructor of the FirstController unless you need it at every controller-hit.
I like lazy loading, so it will load only when needed.
If your AddressController and OrderController has same parent class, than move these methods to parent:
class AddressContoller extends Controller {
public function test() {
$this->methodFromParent();
}
}
If not, create new object of that class and call method. Method must be public
class AddressContoller extends Controller {
public function test() {
$order = new OrderController();
$order->publicMethodInOrderClass();
}
}
If your OrderController wants to call a method foo from AccessController, you should think about moving foo somewhere else. That's an good indicator for wrong SRP
There are two possibilities
foo belongs to/is relevant for every Controller and has something to do with controlling: Just move it to the parent class.
foo is relevant to only a few classes: Move it to the class, it belongs to. This could be an helper class, some domain model class, or something else. Maybe you have to intruduce a new class to do this.
After a lot of reseach I finally manage to get a solution! Posting it here so if anyone in future might get help from it:
class FirstController
{
protected $container;
protected $db;
protected $view;
protected $second;
// constructor receives container instance
public function __construct(\Interop\Container\ContainerInterface $container) {
$this->second = new SecondController($container);
$this->container = $container;
$this->db = $this->container->db;
$this->view = $this->container->view;
}
public function LocalFunction(){
$this->second->otherFunction();
//call the functions in other classes as above
}
}

How to mock methods declared statically in PHP when unit testing

I created the following example test case:
<?php
abstract class Model
{
//...
public static function factory($data)
{
$className = get_called_class();
$obj = new $className($data);
return $obj;
}
}
class User extends Model
{
}
class ExampleController
{
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function create()
{
return $this->user->factory(array('name' => 'Jim'));
}
}
class ExampleTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$user = new User(array('name' => 'Jim'));
$modelStub = $this->getMockBuilder('User')
->disableOriginalConstructor()
->getMock();
$modelStub
->method('factory')
->with(array('name' => 'Jim'))
->willReturn($user);
$example = new ExampleController($modelStub);
$this->assertEquals($user, $example->create());
}
}
However I get the following error:
1) ExampleTest::testSomething
PHPUnit_Framework_MockObject_BadMethodCallException:
I seems to work fine when I remove the static keyword, then my test passes. But I want my Model class to also allow, in other cases, the option to call certain methods without having to instantiate first:
// when instantiation is required
$userModel = new User();
$user = $userModel->factory(array('name' => 'Jim'));
// called statically, no initial instantiation required
$user = User::factory(array('name' => 'Jim'));
I came across this blog which states that methods declared statically, yet called dynamically, is ok. However, methods declared dynamically, yet called statically, will throw a STRICT error - http://www.lornajane.net/posts/2010/declaring-static-methods-in-php
I have also used Laravel's Eloquent before and it appears that both method calls are possible there:
// Eloquent example without initial instantiation is possible too
$user = User::find(1);
Anyway, regardless of whether my code works, I want to be able to mock these methods declared statically. It seems upon reading that PHPUnit just doesn't handle static methods well (I read there was a staticExpects method, but now deprecated as of PHPUnit 3.8). So I'm about to embark on trying some alternative testing frameworks (Codeception and AspectMock, PHPSpec, mockery) as I haven't much experience with others. Would really appreciate some pointers for this issue or advice on the matter as it would really help too in unit testing legacy applications at our company, thanks
The answer is AspectMock.
This library give answers for this questions:
How would you fake the time() function to produce the same result for each test call? Is there any way to stub a static method of a class? Can you redefine a class method at runtime?
You can not call $this->user->factory until you have factory method defined statically. You should change it to User::factory. You can mock such static methods with Moka:
class ExampleController
{
private $_userClass;
public function __construct($userClass = 'User')
{
$this->_userClass = $userClass;
}
public function create()
{
return $this->_userClass::factory(array('name' => 'Jim'));
}
}
class ExampleControllerTest extends \PHPUnit_Framework_TestCase
{
public function testCreateReturnsUser()
{
$userClass = Moka::stubClass(null, ['::factory' => 'USER']);
$controller = new ExampleController($userClass);
$this->assertEquals('USER', $controller->create());
$this->assertEquals(
[[['name' => 'Jib']],
$userClass::$moka->report('::factory')
);
}
}

PHPUnit: Use Mock to modify return value?

I would like to have a PHPUnit Mock which executes a method like normal, but then modifies the return value in some way before the function returns.
What I have
I have a set of derived classes, similar to below:
abstract class Base
{
abstract protected function getUrl();
public function callUrl() {
$url = $this->getUrl();
// some code to call the URL here
}
}
class Foo extends Base
{
protected function getUrl() {
return "http://www.example.com/Foo";
}
}
class Bar extends Base
{
protected function getUrl() {
return "http://www.example.com/Bar";
}
}
Please note the classes I have are much more complex, and some of the items I have to test have side-effects (such as writing to a database, etc).
The naive, duplicate code approach
If I only had a single derived class (eg; Foo), then I could do the following:
class FooMock extends Foo
{
protected function getUrl() {
return parent::getUrl() . "?sandbox";
}
}
class theTest extends PHPUnit_Framework_TestCase
{
public function testIt() {
$mock = new FooMock();
// assert something
}
}
Unfortunately, this means I would need a specific "Mock" class for each derived class I want to test, all of which perform exactly the same function.
The preferred approach
Instead, I would like to be able to do something like the following:
function callback ($returnValue) {
return $returnValue . "?sandbox";
}
class theTest extends PHPUnit_Framework_TestCase
{
private $mock;
public function testFoo() {
$this->mock = $this->getMockBuilder('Foo')->getMock();
$this->setupMock();
// assert something
}
public function testBar() {
$this->mock = $this->getMockBuilder('Bar')->getMock();
$this->setupMock();
// assert something
}
public function setupMock() {
$this->mock->expects($this->any())
->method('getUrl')
->will($this->postProcessReturnValue('callback'));
}
}
Is this at all possible with PHPUnit?
Update: It was suggested I have an instance of the original class, and an instance of the mock class. Use the original class to get the original return value and modify that. This modified value is then used as the return for the Mock. This is not a feasible way to go about things as the classes are more complex (they have side effects such as writing to the DB).
An example where this would not work;
class Foo extends Base
{
$id = 0;
public function saveToDB() {
$this->id = saveToDBAndReturnId();
}
protected function getUrl() {
if ($this->id > 0) {
return "http://www.example.com/".$this->id;
}
throw new Exception("No ID");
}
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();
Obviously the returned URL would be different between multiple calls. I could always mock saveToDB, but that's starting to feel dirty when all I want to do is post-process the result of getUrl.
PHPUnit allows you to define a stub method that will use a callback to determine what to return.
$this->mock->expects($this->any())
->method('getUrl')
->will($this->returnCallback('callback'));
You can define your callback to call the original class method and modify the return value.
Of course, using mock objects in this way more or less defeats the purpose of having them be "mock" objects, since the mock objects will now rely on the underlying implementation.

Instantiating child classes from parent class (PHP)

I have a class with a factory-pattern function in it:
abstract class ParentObj {
public function __construct(){ ... }
public static function factory(){
//returns new instance
}
}
I need children to be able to call the factory function and return an instance of the calling class: $child = Child::factory(); and preferably without overriding the factory function in the child class.
I have tried multiple different ways of achieving this to no avail. I would prefer to stay away from solutions that use reflection, like __CLASS__.
(I am using PHP 5.2.5 if it matters)
If you can upgrade to PHP 5.3 (released 30-Jun-2009), check out late static binding, which could provide a solution:
abstract class ParentObj {
public function __construct(){}
public static function factory(){
//use php5.3 late static binding features:
$class=get_called_class();
return new $class;
}
}
class ChildObj extends ParentObj{
function foo(){
echo "Hello world\n";
}
}
$child=ChildObj::factory();
$child->foo();
In my humble opinion what you're trying to do makes no sense.
A factory pattern would work like this:
abstract class Human {}
class Child extends Human {}
class Fool extends Human {}
class HumanFactory
{
public static function get($token)
{
switch ($token)
{
case 'Kid' : return new Child();
case 'King': return new Fool();
default : throw new Exception('Not supported');
}
}
}
$child = HumanFactory::get('Kid');

Categories