Phpunit: replace class with mock - php

Is it possible to replace some class instantiation with the mock one so that when I call new SomeClass() the mock one is created with specified mocked behavior?

It is highly not recommended but possible, take a look at links bellow
https://github.com/php-test-helpers/php-test-helpers
https://github.com/krakjoe/uopz
It's better to use Dependency Injection (simply, it means injecting instances as arguments to function instead of creating them inside the function). As a result you will be able easily to mock that argument while testing.
You can google about other pros of DI, but here a few links for you:
answer on SO, DI and phpunit

Related

why is it impossible to test a static method with mockery or anything else

I have read in laravel's facade documentation the following sentence:
Typically, it would not be possible to mock or stub a truly static
class method.
1) question 1: I'm trying to understand facade in laravel. As I guess, it's implemented because if we have classes, and they have big namespaces and big names and every time we want to use this class and we don't want to use new keyword and use statements, we use the facade which is an easier code and readable. I also think that laravel implemented facades because they wanted to write non-static functions in their classes so that they could be tested. After all of this, we use facades like static classes (because of readability and not using new and use), but in reality, it makes new instances.
Am I right?
2) If the above is right, can you provide me an example why it's not possible to test a static method as laravel docs said?
A facade does not solve the big namespaces problem you mentioned. Big namespaces are solved using aliases. You can declare them in your config/app.php and internally Laravel will use class_alias when you call them. This is how e.g. \Cache or \DB work.
A facade is basically a proxy class to a singleton object instance of another class (the facade itself ensures the instance is a singleton).
Typically to register a singleton in Laravel you:
Add app()->singleton(ABC::class) in your service provider
Access it via app()->make(ABC::class)->...
A facade basically takes care of that for you if you haven't already registered that class as a singleton.
Basically a facade is a way to proxy that singleton instance of another class.
Also it's generally not possible to mock or stub static methods however if you are using facades you can do \ABCFacade::swap($mockObject) and therefore your facades can be mocked.
It is also false that you cannot test a static method. You can absolutely test a static method. For example:
public testStaticMethod() {
$this->assertEquals(1, ABC::method()); // We tested a static method against a desired behaviour
}
What you usually can't do is mock a static method. Here's how you would typically mock something with PHPUnit:
public testWithDependency() {
$dependency = $this->getMockBuilder(Dependency::class)->getMock();
$dependency->expects($this->once())->method('dependantMethod')->willReturn(true);
$objectToTest = new ABC($dependency); //We're passing a fake dependency which behaves in an ideal way
$this->assertEquals(1, $objectToTest->methodToTest()); //Any calls to the dependency will call mock methods and not real ones
}
The problem arises when trying to mock a static method. As you can see mocking creates mock instances of a certain type. It can't mock the static members of that type because the mock object itself is not actually of that type.
However as I just found out the statement that it's not possible to mock or stub a static method is not entirely true. There's the AspectMock you can mock static methods or helper methods. This seems to work by intercepting all function calls via a custom autoloader.
This being said, just because you can doesn't mean it's good practice to use static methods, there's other issues to consider like e.g. you normally can't have static interfaces in most programming languages or you normally can't override static methods in most programming languages. Note the "in most programming languages" part here. In PHP it's entirely possible to override static methods with late static binding but that means you need to make a conscious decision about this when implementing the static method.
Another disadvantage is that a class of statics can't implement an interface because interfaces apply to object behaviours and not the static behaviour. Therefore you can't swap out one interface for another if you are using statics which is a major disadvantage.
In general the aversion to static methods is not because of testability but because if you are coding in OOP you are really limited if you are using statics.
Hopefully this will help clear up some confusion.

Create class instances like Laravel

I was looking at some Laravel code for some new ideas for my framework, and I found they declare their class instances like:
Request $request;
Which then acts as a Request class reference, I also noticed that there's a namespace being added in the Laravel code.
I looked all around the PHP Manual, re-read the OOP Manual as well as re-read the namespace section as well, some code I've tried:
// Includes a class called PSM
PSM $psm;
$psm->version();
I understand that'd be under "alternate syntax" structure, and also couldn't find anything under that name either.
I realized this was similar to C# Syntax, whereas when declaring variables or class instances, you give them a specific data-type which with instances is their own class-name. (Might be the wrong definition, the main thing to take from that was the C# similarities of this syntax)
As it might be confusing, I'm talking about the creation of the $request instance from simply typing:
Request $request;
Which then brings the class instance into the scope of the Controller.
You're talking about IoC. You can bind any class using Laravel container and then use this syntax to resolve this class:
function index(Request $request)
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

How to unit test active record pattern static methods?

I'm wondering how I would go about unit testing the active record pattern? I'm using Yii 2 for this project and I understand that normally I would want to make mocks and use dependency injection to insert these mocks into my test class - however, I don't see how I would do this with static methods since I'm not instantiating the class anywhere? For example, in Yii's active record class I would do the following to select a row from the database:
ExampleTable::find()->where(['id' => 10])->one();
Any ideas how I would mock this class that uses static methods to work directly with the database?
In Yii2 exist codeception and you can use library codeception/AspectMock to mock all static without dependency injection:
https://github.com/Codeception/AspectMock
This is example from official AspectMock manual:
Allows stubbing and mocking of static methods.
Let's redefine static methods and verify their calls at runtime.
<?php
function testTableName()
{
$this->assertEquals('users', UserModel::tableName());
$userModel = test::double('UserModel', ['tableName' => 'my_users']);
$this->assertEquals('my_users', UserModel::tableName());
$userModel->verifyInvoked('tableName');
}
I hope it will help.
Couple of ideas:
extract db related stuff to some kind of Repository/DAO class, inject it as a dependency and mock in your test
extract db related stuff to separate methods visible in tests and use partial mocking
go with tests that actually use db, so prepare your data as a part of test setup, run method and verify results based on what was returned from db -> however it may not comply with your unit test definition :)

phpspec force method-returned value

I am trying to test a class method that gets its data from another method.
So I did this :
function it_should_return_json_file_as_array()
{
$this->exist()->willReturn(true);
$this->read()->willReturn("{\"key\":\"value\"}");
$this->getContent()->shouldHaveKeyWithValue('key', 'value');
}
But when I launched phpspec run, I got this:
[InvalidArgumentException]
String "" is not a valid classname.
Please see reference document: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
What is going on?
It is not possible to stub methods of a class you are specifying, and it is not possible on purpose.
PhpSpec is trying to tell you there's a problem with the way you decided to design.
See My top ten favourite PhpSpec limitations - Limitation #2:
This limitation can become very visible in the case of using inheritance to extend behaviour. We inherit a class and want to add a new method that internally delegates some behaviour to a parent class method. We cannot mock or stub that method. This has lead me again and again to favour composition over inheritance, which is a golden principle in OO design. In the few cases in which inheritance is justified, we can isolate the reusable behaviour into a separate object and use composition in the parent object, allowing us to replace the collaborator with a double.
Also read Partial Mocking.

Mock Objects in PHPUnit to emulate Static Method Calls?

I am trying to test a class that manages data access in the database (you know, CRUD, essentially). The DB library we're using happens to have an API wherein you first get the table object by a static call:
function getFoo($id) {
$MyTableRepresentation = DB_DataObject::factory("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
...you get the idea.
We're trying to test this method, but mocking the DataObject stuff so that (a) we don't need an actual db connection for the test, and (b) we don't even need to include the DB_DataObject lib for the test.
However, in PHPUnit I can't seem to get $this->getMock() to appropriately set up a static call. I have...
$DB_DataObject = $this->getMock('DB_DataObject', array('factory'));
...but the test still says unknown method "factory". I know it's creating the object, because before it said it couldn't find DB_DataObject. Now it can. But, no method?
What I really want to do is to have two mock objects, one for the table object returned as well. So, not only do I need to specify that factory is a static call, but also that it returns some specified other mock object that I've already set up.
I should mention as a caveat that I did this in SimpleTest a while ago (can't find the code) and it worked fine.
What gives?
[UPDATE]
I am starting to grasp that it has something to do with expects()
I agree with both of you that it would be better not to use a static call. However, I guess I forgot to mention that DB_DataObject is a third party library, and the static call is their best practice for their code usage, not ours. There are other ways to use their objects that involve constructing the returned object directly. It just leaves those darned include/require statements in whatever class file is using that DB_DO class. That sucks because the tests will break (or just not be isolated) if you're meanwhile trying to mock a class of the same name in your test--at least I think.
When you cannot alter the library, alter your access of it. Refactor all calls to DB_DataObject::factory() to an instance method in your code:
function getFoo($id) {
$MyTableRepresentation = $this->getTable("mytable");
$MyTableRepresentation->get($id);
... do some stuff
return $somedata
}
function getTable($table) {
return DB_DataObject::factory($table);
}
Now you can use a partial mock of the class you're testing and have getTable() return a mock table object.
function testMyTable() {
$dao = $this->getMock('MyTableDao', array('getMock'));
$table = $this->getMock('DB_DataObject', ...);
$dao->expects($this->any())
->method('getTable')
->with('mytable')
->will($this->returnValue($table));
$table->expects...
...test...
}
This is a good example of a dependency in your code - the design has made it impossible to inject in a Mock rather than the real class.
My first suggestion would be to try and refactor the code to use an instance rather than a static call.
What's missing (or not?) from your DB_DataObject class is a setter to pass a prepared db object before calling the factory method. That way you can pass a mock or a custom db object (with the same interface) should the need arise.
In your test setup:
public function setUp() {
$mockDb = new MockDb();
DB_DataObject::setAdapter($mockDb);
}
The factory() method should return the mocked DB instance. If it's not already integrated into your class, you will probably have to refactor the factory() method as well to make it work.
Are you require/including the class file for DB_DataObject in your test case? If the class doesn't exist before PHPUnit tries to mock the object you can get errors like this.
With PHPUnit MockFunction extension plus runkit you can also mock static methods. Be careful, because it's monkey patching and therefore should only be used in extreme cases. Does not substitute good programming practices.
https://github.com/tcz/phpunit-mockfunction

Categories