How to reset expectations on PHPUnit mock object - php

I have a large amount of tests in a TestCase. I want to set up a mock object that returns the same value in most of the tests, but in a few of the tests I would like to customize that value.
My idea was to create a set_up() method (I was unable to set expectations inside the automatically invoked setUp()), and to manually call it at the beginning of each test. In this method, I would set the default return value, then in the few tests that need to customize the return value, I would call expects a second time, and hopefully overwrite the default return value. This does not work, the return value is not overwritten.
Here is a simplified example:
<?php
class SomeClass {
function someMethod() {
}
}
class SomeTest extends PHPUnit_Framework_TestCase {
private $mock;
function set_up() {
$this->mock = $this->getMockBuilder('SomeClass')
->disableOriginalConstructor() // This is necessary in actual program
->getMock();
$this->mock->expects($this->any())
->method('someMethod')
->will($this->returnValue(1));
}
function test() {
$this->set_up();
$this->mock->expects($this->any())
->method('someMethod')
->will($this->returnValue(2));
$this->assertEquals(2, $this->mock->someMethod());
}
}
It seems that this should be possible from reading How to reset a Mock Object with PHPUnit.
PHPUnit mock with multiple expects() calls does not answer my question.
I am using phpUnit 4.2

You can pass arguments to set_up method so it can configure the mock as needed:
function set_up($someMethodReturnValue = 1) {
$mock = $this->getMockBuilder('SomeClass')
->disableOriginalConstructor() // This is necessary in actual program
->getMock();
$mock->expects($this->any())
->method('someMethod')
->will($this->returnValue($someMethodReturnValue));
return $mock;
}
function test() {
$mock = $this->set_up(2);
$this->assertEquals(2, $this->mock->someMethod());
}
You can further enhance the set_up() method. Eventually you could create a Mock creation class if there are lots of options.

Related

PHPUnit mock - call parent __get/__set/__isset

I am trying to mock a class that has inherited magic methods, but they are not getting implemented, and I have no idea how to fix it.
Here's the code:
<?php
use PHPUnit\Framework\TestCase;
class AnnoyingTestCase extends TestCase
{
public function testNoAttributes()
{
$mock = $this->createMock(Child::class);
$mock->attribute = 'value';
$this->assertEquals($mock->attribute, null);
}
}
class Base
{
private $attributes = [];
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
public function __get($name)
{
return $this->attributes[$name] ?? null;
}
public function __isset($name)
{
return isset($this->attributes[$name]);
}
}
class Child extends Base
{
}
I would expect the $mock->attribute to have the value I assigned it, but it does not because __set is not being called.
If I try to use ->addMethods(['__get', '__set']), then it throws an exception: Trying to set mock method "__get" with addMethods(), but it exists in class "Child". Use onlyMethods() for methods that exist in the class..
And yet adding a var_dump inside the __set method shows that it is not being called.
What can I do so that it has these methods?
Note that I am not looking to implement my own equivalent of these methods, I want the mock to have these original methods.
Found a fix for this exact issue, but it may cause more harm than good.
The following code works:
$mock = $this->getMockBuilder(Child::class)
->enableProxyingToOriginalMethods()
->getMock();
There is, however, the problem that this proxies ALL original method calls, and there is no way to narrow down the methods that you want to proxy. And the biggest - potentially broken constructor calls - is described here: PHPUnit - MockBuilder::enableProxyingToOriginalMethods() breaks when original constructor calls public method

PHPUnit: Is it possible to mock a php function to return a mocked object in replacement of the normal object?

I have a constructor in the class someClass that calls a function (initObjectFunc), which its only purpose is to return a newly created instance of Thing class, and places its that object into a property (thingObject) in the constructor.
class someClass {
public function __construct() {
$this->thingObject = $this->initObjectFunc();
}
function initObjectFunc() {
return new Thing();
}
}
class Thing {
public function __construct() {}
function funcB() {
... funcB normal implementation ...
return value;
}
}
I also have a separate function (funcA), also in the someClass class, that uses thingObject to call its inner function (funcB).
.
.
.
function funcA() {
$data = $this->thingObject->funcB();
... other stuff ....
}
In a PHPUnit test class, I had mocked the Thing class and its funcB to return mocked data ('test test test test') when it gets called through the execution of funcA. To connect all the pieces together, I mocked the initObjectFunc to return that mocked Thing class object.
class unitTest extends PHPUnit_Framework_TestCase {
$mockThing = $this->getMockBuilder('Thing')
->setMethods(array('funcB'))
->getMock()
->expects($this->any())
->method('funcB')
->will($this->returnValue('test test test test'));
$mockSomeClass = $this->getMockBuilder('someClass')
->setMethods(array('initObjectFunc'))
->getMock()
->expects($this->any())
->method('initObjectFunc')
->will($this->returnValue($mockThing));
}
This setup was created with this execution flow in mind:
$mockSomeClass calls-> funcA() calls-> *mocked*funcB through *mocked*Thing class
Use the $mockSomeClass to call funcA (which, in PHPUnit, should not have been stubbed and should have full functionality), which would call the funcB. However, since I mocked Thing class and its funcB, it should be calling the mocked funcB to return 'test test test test'.
This is not the case when I try to run the tests. It breaks without any errors. Not even the line that says how many tests, assertions, skips, & fails have occurred.
I had also tried this variation, but to no avail:
class someClass {
public function __construct() {}
function initObjectFunc() {
return new Thing();
}
function funcA() {
$thingObject = initObjectFunc();
$data = $thingObject->funcB();
... other stuff ....
}
}
This variation tries to take the functionality from the constructor and move it into funcA so that the initObjectFunc() gets called and should return a mocked Thing instance before using it to call funcB. This was done under the thought that maybe the someClass constructor gets called before any of the mocks can be made.
I don't know why or how these components are not connecting. Has anyone tried unit testing with this structure and achieved success? I know that a simple refactor of code to become "dependency injection"-friendly would solve this issue, but this is a question just to find out if this path to unit testing works.

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 seemingly not calling public function

Code:
MyMockClass.php
<?php
class MyMockClass
{
public function __construct($l)
{
// Do nothing with it
}
protected function loadData($var)
{
// Do something, it doesn't matter what
return null;
}
public function onEvent($key)
{
return $this->loadData($key);
}
}
MockTest.php
<?php
class MockTest extends \PHPUnit_Framework_TestCase
{
public function testPHPUnitMock()
{
$mock = $this->getMockBuilder('MyMockClass')->setConstructorArgs(array(true))->getMock();
$mock->expects($this->once())->method('loadData')->with('TEST')->will($this >returnValue(true));
$this->assertEquals(true, $mock->onEvent('TEST'));
}
}
When I run this test, it fails, with the following output:
PHPUnit_Framework_ExpectationFailedException : Failed asserting that null matches expected true.
Expected :true
Actual :null
I'm trying to execute onEvent, which in turn executes a function I have mocked, and changed the outcome of. But the onEvent function doesn't seem to get called. If I put mail() (or something similar) in onEvent, I don't receive any mail.
If you don't tell the mock builder which methods you are going to mock - all methods from the mocked class will be mocked by default. 
In your case when you invoke onEvent method in your test, actually you've invoked a mocked method.
If you specify method you want to mock, then the mock builder will mock only these method and leave the rest as they are in original class.
So, try build your mock in that way:
public function testPHPUnitMock()
{
$mock = $this->getMockBuilder('MyMockClass')
->setMethods(array('loadData')) // this line tells mock builder which methods should be mocked
->setConstructorArgs(array(true))
->getMock();
$mock->expects($this->once())->method('loadData')->with('TEST')->will($this->returnValue(true));
$this->assertTrue($mock->onEvent('TEST'));
}

PHPUnit testing with closures

This came up trying to write a test for a method of a class that calls a mock method with a closure. How would you verify the closure being called?
I know that you would be able to assert that the parameter is an instance of Closure. But how would you check anything about the closure?
For example how would you verify the function that is passed:
class SUT {
public function foo($bar) {
$someFunction = function() { echo "I am an anonymous function"; };
$bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase {
public function testFoo() {
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->with( /** WHAT WOULD I ASSERT HERE? **/);
$sut = new SUT();
$sut->foo($mockBar);
}
}
You can't compare two closures in PHP. Is there a way in PHPUnit to execute the parameter passed in or in some way verify it?
If you want to mock an anonymous function (callback) you can mock a class with __invoke method. For example:
$shouldBeCalled = $this->getMock(\stdClass::class, ['__invoke']);
$shouldBeCalled->expects($this->once())
->method('__invoke');
$someServiceYouAreTesting->testedMethod($shouldBeCalled);
If you are using latest PHPUnit, you would have to use mock builder to do the trick:
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->setMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke');
$someServiceYouAreTesting->testedMethod($shouldBeCalled);
You can also set expectations for method arguments or set a returning value, just the same way you would do it for any other method:
$shouldBeCalled->expects($this->once())
->method('__invoke')
->with($this->equalTo(5))
->willReturn(15);
Your problem is that you aren't injecting your dependency (the closure), which always makes unit testing harder, and can make isolation impossible.
Inject the closure into SUT::foo() instead of creating it inside there and you'll find testing much easier.
Here is how I would design the method (bearing in mind that I know nothing about your real code, so this may or may not be practical for you):
class SUT
{
public function foo($bar, $someFunction)
{
$bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase
{
public function testFoo()
{
$someFunction = function() {};
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->with($someFunction);
$sut = new SUT();
$sut->foo($mockBar, $someFunction);
}
}
If the closure has some side effects inside SUT that could be verified by the test after the mock invocation, use returnCallback to provide another closure to be called with the passed arguments and have its return value returned to SUT. This will allow you to call SUT's closure to cause the side effects.
class SUT {
public function foo($bar) {
$someFunction = function() { return 5 * 3; };
return $bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase {
public function testFoo() {
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->will($this->returnCallback(function ($someFunction) {
return $someFunction();
}));
$sut = new SUT();
self::assertEquals(15, $sut->foo($mockBar));
}
}

Categories