How to mock methods declared statically in PHP when unit testing - php

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')
);
}
}

Related

Unable to mock any custom class method in laravel 6

I have a Basic Class Helper.
namespace App\Helpers;
class CommonUtility {
public static function foo() {
return 'foo';
}
}
And it's used in User.php
<?php
class User extends Authenticatable
public function test() {
return CommonUtility::foo();
}
}
And now I wanted to write a test with mocking CommonUtility class. Can anyone know about this I'm new to laravel unit test.
Tried few solutions like
$mock = Mockery::mock(CommonUtility::class, function ($mock) {
$mock->shouldReceive('foo')->andReturn('foobar');
})->makePartial();
dd($mock->foo());
it gives me the mock result but i tried it from
$user = new \App\User();
dd($user->test());
It's calling the actual function instead of mock one.
You could use the Aliasing Mockery features for Mocking Public Static Methods
As example:
$mock = \Mockery::mock('alias:App\Helpers\CommonUtility');
$mock->shouldReceive('foo')
->andReturn('foobar');
$user = new User();
$this->assertEquals("foobar", $user->test());
The test pass as expected

Make own loadhelper function (like in CodeIgniter)

I'm trying make every day something new. Now i want to make function to dynamic load helper without include and creating new class object
In code igniter that's look like
$this->load->helper('syslog_helper');
and now we can use
syslog_helper->some_function()
file is automatic including, object is automatic created and we can use them
Question is: how can I do the same think using pure PHP ?
Like this
Create a class name load that has a method named helper, if you want it accessible with $syslog_helper then load needs to be able to call the original class so when you create an instance pass the $this as part of its constructor. Then the main class should use the magic __set method etc
Helper class to load:
class syslog_helper{
}
Loader class:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once HELPER_DIR.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$class = new $class;
}
}
Base controller class
class foo{
public $data = [];
public $load;
public function __construct(){
//create the loader instance, pass an instance of the controller (this)
$this->load = new loader($this);
}
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
}
public function __set($key, $value){
//create a dynamic property
$this->data[$key] = $value;
}
public function __get($key){
//get a dynamic property
return $this->data[$key];
}
}
Call it:
(new foo)->bar();
Output:
syslog_helper Object
(
)
Sandbox
As you can see above, $this->syslog_helper gets populated with our helper class just like CI does it.
So it flows in this order:
$foo = new foo - create instance of controller, assign the loader class (with a back reference to the controller) $this->load = new loader($this);
$foo->bar() - call bar() this would be the request function in the controller, such as what the URL routes to.
$foo->load->helper('syslog_helper') - use the load property (instance of loader) to call its helper method, passing the name of the helper class as a string. Helper method should require the class file, and then create an instance of that class. new $class
$this->obj->$class = new $class; - then that instance is assigned to a dynamic property named the same as what was passed in
$this->obj->$class - Controller's __set magic method is triggered, storing instance of helper in Controler->data[$helper]
$foo->syslog_helper() - Controller's __get magic method is triggered returning Controler->data[$helper] or the instance of the helper we just created.
I just made this up, but I am sure CI is similar. You could look in the parent class of the Controllers etc.. and see how they do it.
Hope that makes sense...
One simple improvement you could make for the above code
I think CI does this, is to allow aliasing of the property... like this:
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function helper($class, $alias = null){
//if no alias default to the class name
if(!$alias) $alias = $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.'helpers/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
Now if we did this in the controller's bar method:
class foo{
public function bar(){
//call the loader's helper() method with the name of the helper class
$this->load->helper('syslog_helper');
print_r($this->syslog_helper);
$this->load->helper('syslog_helper', 'syslog_helper_2');
print_r($this->syslog_helper_2);
}
Output:
syslog_helper Object
(
)
syslog_helper Object
(
)
You now have 2 instances of the helper, one named syslog_helper and the other syslog_helper_2. If we didn't alias them, the second call would simply overwrite the property in the controller leaving us with just one instance.
So you can see above we added a whole lot of flexibility with essentially 1 line of code. Big improvements don't have to be complex.
Sandboxy
Obviously you should flesh this out a bit more. By adding more things, like error checking for classes (files) that don't exist, __unset and __isset magic methods etc... But, this is the basic functionality you wanted.
Similarly you can add, model and library methods with the only real difference being the location. For that I would probably go with the magic __call method instead of 3 functions that do the same.
Implement load->model, load->library and load->helper
class loader{
protected $obj;
public function __construct($obj){
//save the controller instance as a property
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
$this->obj->$alias = new $class;
}
}
Sandbox
Implement singletons
A singleton is basically re-using the same instance of the class for future calls, you can implement this with a few more changes to loader:
class syslog_helper{
public $test;
}
class loader{
protected $obj;
protected static $instances = [];
public function __construct($obj){
$this->obj = $obj;
}
public function __call($method, $args){
//type => folder
$allowed = ['helper'=>'helpers','model'=>'models', 'library'=>'libraries'];
if(!isset($allowed[$method])) throw new Exception('Unknown method '.$method);
if(!isset($args[0])) throw new Exception('Missing required argument for method '.$method);
$class = $args[0];
$alias = isset($args[1]) ? $args[1] : $class;
//if this is the first time we instantiated [$method][$alias] save it
if(!isset(static::$instances[$method][$alias])){
//require the class file - don't need to in this case as they are all in the same file for sandbox
//require_once APPPATH.$allowed[$method].'/'.$class.'.php';
//create a new instance of Helper and assign it back to the controller
static::$instances[$method][$alias] = new $class;
}
//return the saved static instance
$this->obj->$alias = static::$instances[$method][$alias];
}
}
class foo{
public $data = [];
public $load;
public function __construct(){
$this->load = new loader($this);
}
public function bar(){
$this->load->helper('syslog_helper');
print_r('bar::Test before: '.$this->syslog_helper->test."\n");
$this->syslog_helper->test = 'test';
print_r('bar:Test after: '.$this->syslog_helper->test."\n");
}
public function biz(){
$this->load->helper('syslog_helper');
print_r('biz:Test: '.$this->syslog_helper->test."\n");
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key){
return $this->data[$key];
}
}
$foo = new foo;
$foo->bar();
$foo->biz();
Output:
bar::Test before:
bar:Test after: test
biz:Test: test
Sandbox
The important thing here is that, when we call $this->load->helper('syslog_helper'); from biz() in the controller we are getting the same instance of the helper we created previously. You can tell this because the public property I added to the helper retains it's value we set in bar(). You could actually call this anywhere in your code and get the same instance with the same data stored in it, it was just easier (shorter) for examples sake to do it this way.
This is useful if you need the same helper in multiple classes, instead of creating multiple instances you can re-use them. I am not sure if CI does this right off the top of my head... lol
In this case I think it's acceptable to do them as a singleton, if you need a new copy you can just alias it and then that would be a independent instance.
One last thing I should add, is that CI probably doesn't pass the controller instance to the loader class. This is because CI calls the controller from the routing so it already has the instance of the Controller at it's disposal. And as CI is a singleton it can probably be accessed with $CI = get_instance(); inside of loader, so there is no need to pass it along in the the way I showed inside of the CI framework. Basically they just access the same data in a different way.
Cheers!

What's the difference between Laravel automatic injection and manually specifying the dependencies in the constructor body?

I'm using a Repository pattern in my Laravel project. This pattern is not really explained in the official documentation, except for this snippet:
You may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class.
This is my code, in accordance with the documentation:
class CategoriesController extends Controller
{
protected $repo;
public function __construct(CategoriesRepository $repo)
{
$this->repo = $repo;
}
I've type-hinted the CategoriesRepository so it gets automatically loaded by the Service Container.
However, if I directly create a new instance of the CategoriesController class (without using the Service Container), I have to specify that I need a new instance of the CategoriesRepository too, like this:
$example = new CategoriesController(new CategoriesRepository());
Now, let's suppose I write the following code.
class CategoriesController extends Controller
{
protected $repo;
public function __construct()
{
$this->repo = new CategoriesRepository();
}
This way, I don't have to load the class through the Service Container, nor call it by passing a new instance of CategoriesRepository as the argument, because it's automatically created inside of the constructor.
So, my question is: would this be bad practice? What's the difference between type-hinting as a parameter and creating a new instance inside of the constructor?
Here's the beauty of dependency injection:
Complex initialization
class MyController {
public function __construct(A $a) { }
}
class A {
public function __construct(B $b) { }
}
class B {
public function __construct(C $c) { }
}
class C {
public function __construct(D $d) { }
}
class D {
public function __construct() { }
}
Now you can ask laravel to create that class for you e.g:
$controller = make(MyController::class);
or you can do:
$controller = new MyController(new A(new B(new C(new D())))));
In addition you can specify more complex rules on how to create the variables:
app()->bind(D::class, function ($app) {
$d = new D();
$d->setValueOfSomething($app->make(AnotherClass::class));
return $d;
});
Testing
That's one advantage of dependency injection over manual creation of things. Another is unit testing:
public function testSomeFunctionOfC() {
$this->app->bind(D::class, function () {
$dMock = $this->createMock(D::class);
});
$c = make(C::class);
}
Now when you create C the class D will be the mocked class instead which you can ensure works according to your specification.

Unable to test static method using Mock class in PHPUnit

I'm trying to test a static method on a class, but it relies on a static array property that doesn't get reset between tests, so I though I could create a mock class & test against that.
The only problem is that the mock class static method is not returning the value as the original static method does.
Here's my class...
class Thing {
static public function doThing() {
return 'yeah!';
}
}
... and here's be test class...
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
This test fails with the message "Failed asserting that null matches expected 'yeah!'."
What am I missing here? I thought not specifying the methods on the getMock() call meant that the original methods carried over, but clearly not. :o(
Any pointers would be great. Thanks.
Edit
I typed this up from memory, rather than from copying code, as I'd made further change to try to resolve this. After writing more tests for mock objects I realised that if you pass array() as the second parameter ($methods) to getMock[Class] then it stubbed out all the methods, & I believe this is what I had actually done. I was doing this, as I also wanted to pass the constructor parameter in the 3rd argument. The code probably looked more like this...
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass(
'Thing',
array(),
array( 'something' )
);
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
Maybe $mock_class is an object, not a string class name? Try to use next:
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', Thing::doThing())
}
}
Is this work?
You forgot extending by PHPUnit_Framework_TestCase.
class ThingTest extends PHPUnit_Framework_TestCase {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
You shouldn't mock the class containing the method you want to test. If you mock the class, you can't call any method either before you haven't defined it via
-> expects('methodName')
-> with($this->equalTo($parameter),[$this->equalTo($parameter2),...])
-> will($this->returnValue($value))
. Mocking is used to reduce complexity of a test. Let's say you want to test a function that looks like this:
class test{
private $someClass;
public function __construct(SomeInterface $someClass){
$this -> someClass = $someClass;
}
public function test(){
$someVariable = $this -> someClass -> doSomething();
changeSomething($someVariable);
return $someVariable;
}
}
If you don't mock someClass, you'd have to test the whole code of someClass -> doSomething. But that is not what you want to do. You just want to use the return of the function doSomething. That is the point where you need a mock. It reduces the complexity of your test and offers REAL UNIT tests instead of testing the whole app.
So what you need to do is:
class ThingTest {
public function testDoSomething() {
$class = new Thing();
$this->assertEquals('yeah!', $class::doThing());
}
}
There is no need for a mock.

PHPUnit Mock Objects and Static Methods

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...

Categories