What is the difference between overload and alias in Mockery? - php

I am new to using Mockery and confused with the terminology alias and overload. Can anyone please explain to me when to use which?

Overload is used to create an "instance mock". This will "intercept" when a new instance of a class is created and the mock will be used instead. For example if this code is to be tested:
class ClassToTest {
public function methodToTest()
{
$myClass = new MyClass();
$result = $myClass->someMethod();
return $result;
}
}
You would create an instance mock using overload and define the expectations like this:
public function testMethodToTest()
{
$mock = Mockery::mock('overload:MyClass');
$mock->shouldreceive('someMethod')->andReturn('someResult');
$classToTest = new ClassToTest();
$result = $classToTest->methodToTest();
$this->assertEquals('someResult', $result);
}
Alias is used to mock public static methods. For example if this code is to be tested:
class ClassToTest {
public function methodToTest()
{
return MyClass::someStaticMethod();
}
}
You would create an alias mock using alias and define the expectations like this:
public function testNewMethodToTest()
{
$mock = Mockery::mock('alias:MyClass');
$mock->shouldreceive('someStaticMethod')->andReturn('someResult');
$classToTest = new ClassToTest();
$result = $classToTest->methodToTest();
$this->assertEquals('someResult', $result);
}

Additional info to Fredrik Schöld's answer:
Alias mocking is persistent per test cases, so you need to deactivate it each time you use it. Just add this phpDoc to each test class there you use alias mocking:
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/

I found this:
https://github.com/padraic/mockery-docs/blob/master/reference/startup_methods.rst
$mock = \Mockery::mock('alias:MyNamespace\MyClass');
Prefixing the valid name of a class (which is NOT currently loaded) with "alias:" will generate an "alias mock". Alias mocks create a class alias with the given classname to stdClass and are generally used to enable the mocking of public static methods. Expectations set on the new mock object which refer to static methods will be used by all static calls to this class.
$mock = \Mockery::mock('overload:MyNamespace\MyClass');
Prefixing the valid name of a class (which is NOT currently loaded) with "overload:" will generate an alias mock (as with "alias:") except that created new instances of that class will import any expectations set on the origin mock ($mock). The origin mock is never verified since it's used an expectation store for new instances. For this purpose we use the term "instance mock" to differentiate it from the simpler "alias mock".
So to me it seems that overload does the same as alias with the difference that it also imports expectations from the origin mock.

Related

How to instantiate classes based on type in laravel?

So I have a BaseValuation abstract class and it's implementations example FooValuation and BarValuation.
I want to instantiate Foo or Bar implementations depending on use input. So I created a simple class myself called Valuation which simply does this:
<?php
namespace App\Valuation;
class Valuation
{
public $class;
public function __construct($id, $type = 'basic')
{
$class = 'App\Valuation\Models\\' . ucfirst($type) . 'Valuation';
$this->class = new $class($id);
}
public function make()
{
return $this->class;
}
}
Using this I can simply do (new App\Valuation\Valuation($id, $type))->make() and I will get the desired implementation according to what the use asked for.
But I know that laravel's container is powerful and must allow me to do this somehow but I cannot understand how this will be done. Any ideas anyone?
You can bind a class to any string, normally this is done in a service provider.
$this->app->bind('string', SomethingValuation::class);
Then you can instantiate this with App::make('string').
I think this has more value when you're binding a single implementation (concrete) to an interface (abstract) rather than binding multiple classes.
You can also just allow Laravel to instantiate the class for you by calling App::make(\Full\Path::class).
Both would allow you to inject a mock into the container with the same name for testing purposes.

PHPUnit: mock all functions except one in ZendFramework2 Controller

I need to mock a zf2 controller and keep one real function: "getStopWords".
I tried this answer adapted below:
public function createMockExecpt()
{
// create mock to get names of all functions
$mock = $this->getMockBuilder('Controller\CollectionsController')->disableOriginalConstructor()->getMock();
$reflection = new MyReflectionClass($mock);
$functionsToMock = $reflection->getAllfunctionNamesExcept(["getStopWords"]);
// create mock but don't mock one function
return $this->getMock('Controller\CollectionsController', $functionsToMock);
}
but got an error about redefining a class.
// Cannot redeclare Mock_CollectionsController_d61a5651::__clone()
I think this happens because I need an instance of the controller to find out all the functions it has. However I can't make an instance of the controller in this context, which is why I need a mock. But I can't make more than one mock of a class within the same test, so I'm stuck.
My problem was that I thought you need an instance of the class to get all the methods.
Turns out all you need is the class name!
public function testGetStopWords()
{
// get the class methods the controller has, except getStopWords
$methodsToMock = array_diff(
get_class_methods("Controller\CollectionsController"),
["getStopWords"]
);
// use setMethods to determine which methods to mock
$mockController = $this->getMockBuilder("Controller\CollectionsController")
->setMethods($methodsToMock)
->disableOriginalConstructor()
->getMock();
}

How can I set protected properties of mocks for abstract classes?

I've looked around and I found a solution that works for normal objects, but it doesn't seem to work for mocks.
The test below fails with the message: Unable to set property someProperty of object type Mock_ClassToTest_40ea0b83: Property someProperty does not exist.
class sampleTestClass extends PHPUnit_Framework_TestCase
{
function test() {
$object = $this->getMockForAbstractClass(ClassToTest::class, [], '', false);
$this->setProtectedProperty($object, 'someProperty', 'value');
}
private function getReflectionProperty($object, $property) {
$reflection = new ReflectionClass($object);
$reflectionProperty = $reflection->getProperty($property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty;
}
/**
* This method modifies the protected properties of any object.
* #param object $object The object to modify.
* #param string $property The name of the property to modify.
* #param mixed $value The value to set.
* #throws TestingException
*/
function setProtectedProperty(&$object, $property, $value) {
try {
$reflectionProperty = $this->getReflectionProperty($object, $property);
$reflectionProperty->setValue($object, $value);
}
catch ( Exception $e ) {
throw new TestingException("Unable to set property {$property} of object type " . get_class($object) .
': ' . $e->getMessage(), 0, $e);
}
}
}
abstract class ClassToTest
{
private $someProperty;
abstract function someFunc();
}
class TestingException extends Exception
{
}
EDIT: 8/31/2016 4:32 PM EST
Updated code in response to answer by Katie.
You are trying to call Reflection methods on a mocked object, instead, you can call it on the abstract class itself:
So change:
$reflection = new ReflectionClass(get_class($object));
to
$reflection = new ReflectionClass(ClassToTest::class);
And that will work for anything that is not abstract in the class, such as your property, or another method that is fully implemented.
Additional Note since OP was updated
The fix will still work for your first line in the getReflectionProperty. But if you don't have access to the class name, then that is a problem.
Using reflection to access protected and private properties and methods of classes in tests seems a very clever method but it leads to tests that are difficult to read and understand.
On the other hand, only the public interface of a class should be tested. Testing (and even caring about) the protected and private properties and methods of the tested class is a sign that the tests are written after the code. Such tests are brittle; any change in the implementation of the tested class breaks the tests, even when it doesn't break the functionality of the class.
There is usually no need to test an abstract class. Most of the times the tests of its children classes cover the relevant code of the abstract class too. If they doesn't cover some part of it then either that code is not needed there or the test cases do not cover all the corner cases.
However, sometimes one needs to write a testcase for an abstract class. The best approach, in my opinion, is to extend the abstract class at the bottom of the file that contains the test case, provide simple implementations for all its abstract methods and use this class as SUT.
Something along these lines:
class sampleTestClass extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$object = new ConcreteImplementation();
$result = $object->method1();
self::assertTrue($result);
}
}
class ConcreteImplementation extends AbstractClassToTest
{
public function someFunc()
{
// provide the minimum implementation that makes it work
}
}
You are testing a mock in the code you posted. The mocks are not meant to be tested. Their purpose is to simulate the behaviour of the collaborators of the SUT that are not appropriate to be instantiated in a test.
The reasons why a collaborator class is mocked in a test include, but are not limited to:
difficult creation; for example, when the constructor of the mocked class requires many arguments or other objects;
the collaborator is an abstract class or an interface; the actual implementation might not even exist when the test and the tested class are written;
the code of the collaborator takes a lot of time to complete or requires additional resources (disk space, database connection, Internet connection etc);
the code of the collaborator has permanent side effects; this is usually coupled with the previous reason.

PHPUnit mock class that has named static constructor

Given I have a FruitSalad class (the system under test):
class FruitSalad
{
protected $fruits = [];
public function addFruit(Fruit $fruit)
{
$this->fruits[] = $fruit;
return $this;
}
}
And I have a Fruit class:
class Fruit
{
public static function withName($name)
{
$instance = new MyDependencyClass();
$instance->name = $name;
return $instance;
}
}
A trivial example, however you can see that the Fruit class uses a named static constructor, and the addFruit() method on the FruitSalad class type hints Fruit as its expected parameter.
When writing a test for addFruit(), I need to mock the Fruit class.
function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
$fruit = $this->getMockBuilder('Fruit')
->disableOriginalConstructor()
->getMock();
$this->fruitSalad->addFruit($fruit);
// Do some assertion.
}
This creates a simple mock of the Fruit class, but I want to instantiate it via the withName() static method - and I do not want to expose a setter for the name property.
How can I create a mock for Fruit using the static named constructor?
PHPUnit used to support mocking static methods, but since PHPUnit 4.0 it's omitted. I see four options here:
1. Don't mock the method at all
You could just call the method and use it's logic, although you'd test the static method as well if you do and normally that's something you should avoid when writing unit tests.
2. Change the class
Ask yourself if this method really needs to be static and if not, change the class to test it properly. There are quite some use cases where it's better to change some of your architecture in order to write proper tests.
3. Use a spy class
Spy classes are classes that extend a class that you would usually mock, but implement some logic for testing the configuration of a class or the dependency of a tested method to another method. In the very most cases this can be avoided by mocking the class properly. Spies are simply your work around if mocks are not enough, there are very few cases in which you really need them.
However, in this case a spy could be used to overwrite a static method as a work around:
class FruitSpy extends Fruit
{
public static $return;
public static $name;
public static function withName($name) {
$expected = self::$name;
if($name == $expected) {
return self::$return;
} else {
throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
}
}
}
This example checks for the correct $name and, if it's correct, returns your defined return. You'd use it like this in your test:
$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();
$this->fruitSalad->addFruit($fruitSpy);
Not exactly a clean solution, but the only way I see if you absolutely positively don't want to change other code than the test code.
Again, you should think about changing the static method to a casual method if you need to do something like this.
4. Use PHPUni 3.*
You could simple use a deprecated version of PHPUnit to use this method. Not a preferred way either.
Conclusion
I don't see a clean way to mock a static method and ::staticExpects() was removed for a reason in 4.0
How can I create a mock for Fruit using the static named constructor?
You can't. Mocks are created by using a mocking framework.
Anyway it does not matter how mocks are created but, instead, how they behave, because they're external to the class being tested.
Just configure the mock so that it behaves the same way a real Fruit instance would when created using Fruit::withName.

PHPUnit turning an instance of a class into a mock after instantiation

Is there a way to create a mock class with PHPUnit which I can then create a new instance of by using its class name?
I have an interface which defines two methods. Something like:
interface FooInterface {
function getA();
function getB();
}
I then have another class which accepts a class name, creates an instance of that class, checks if it is an instance of what it expects (FooInterface) and then calls two methods on that class to get some information.
class FooInfo {
protected $a;
protected $b;
public function __construct($fooClass) {
$foo = new $fooClass;
if (!($foo instanceof FooInterface)) {
throw new \Exception();
}
$this->a = $foo->getA();
$this->b = $foo->getB();
}
}
I know how to mock an object just fine. The problem is, since this class accepts a class name, not an object (it is a part of a Manager which creates instances of the given class as needed), I can't use a normal mock object.
I tried to make a mock object then use that class name. It seems to create the object just fine, and even seems to have the functions I mocked out. However, it doesn't seem to follow the will($this->returnValue('myValue')) portion I set up later.
public function testConstruct()
{
$foo = $this->getMockForAbstractClass('Foo', array('getA', 'getB'));
$foo->expects($this->any())->method->('getA')->will($this->returnValue('a'));
$foo->expects($this->any())->method->('getB')->will($this->returnValue('b'));
$copyClass = get_class($foo);
$copy = new $copyClass();
// Passes
$this->assertTrue(method_exists($copy, 'getA');
// Fails, $copy->getA() returns null.
$this->assertEquals($copy->getA(), $foo->getA());
}
So, it does have the functions which were mocked, but they all return null.
Any ideas?
To use the new keyword in the constructor of a class is a rather bad habit exactly for the reasons you're experiencing now, even given your flexible use case.
Your test will not work because the mock you create will never be used, since your class will always create a real instance of the injected classname.
That said, what you want to do can be done with padraic's excellent mocking library mockery!
What you need is an 'instance mock':
$mock = \Mockery::mock('overload:MyNamespace\MyClass');
You can define your expectations on that mock which will be transferred to the real object, as soon as it is instantiated.
Integrating mockery with phpUnit is easy and well explained in the readme of the project.
And btw. each unit test should optimally make one assertion only!
The methods getA and getB return null because you have not specified what they should return. You did specify that for the abstract $foo by calling some methods. There's no way around that.
Since it's hard (if not impossible) to test the function, I would rewrite the code itself. Testing would be easy if your constructor would require a class instance rather than a class name. If you also must accept strings, you could write a few lines to check for a string input and create a class if a string is provided.

Categories