PHPUnit: Mocking __get() results in "__get() must take exactly 1 argument ..." - php

I've got a problem with mocking an overloaded __get($index) method.
The code for the class to be mocked and the system under test that consumes it is as follows:
<?php
class ToBeMocked
{
protected $vars = array();
public function __get($index)
{
if (isset($this->vars[$index])) {
return $this->vars[$index];
} else {
return NULL;
}
}
}
class SUTclass
{
protected $mocky;
public function __construct(ToBeMocked $mocky)
{
$this->mocky = $mocky;
}
public function getSnack()
{
return $this->mocky->snack;
}
}
Test looks as follows:
<?php
class GetSnackTest extends PHPUnit_Framework_TestCase
{
protected $stub;
protected $sut;
public function setUp()
{
$mock = $this->getMockBuilder('ToBeMocked')
->setMethods(array('__get')
->getMock();
$sut = new SUTclass($mock);
}
/**
* #test
*/
public function shouldReturnSnickers()
{
$this->mock->expects($this->once())
->method('__get')
->will($this->returnValue('snickers');
$this->assertEquals('snickers', $this->sut->getSnack());
}
}
Real code is a little bit more complex, though not much, having "getSnacks()" in its parent class. But this example should suffice.
Problem is I get the following error, when executing the test with PHPUnit:
Fatal error: Method Mock_ToBeMocked_12345672f::__get() must take exactly 1 argument in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php(231)
When I debug I can't even reach the test method. It seems it breaks at setting up the mock object.
Any ideas?

__get() takes an argument, so you need to provide the mock with one:
/**
* #test
*/
public function shouldReturnSnickers()
{
$this->mock->expects($this->once())
->method('__get')
->with($this->equalTo('snack'))
->will($this->returnValue('snickers'));
$this->assertEquals('snickers', $this->sut->getSnack());
}
The with() method sets the argument for the mocked method in PHPUnit. You can find more details in the section on Test Doubles.

It's a bit hidden in the comments, but #dfmuir's answer put me on the right track. Mocking a __get method is straight forward if you use a callback.
$mock
->method('__get')
->willReturnCallback(function ($propertyName) {
switch($propertyName) {
case 'id':
return 123123123123;
case 'name':
return 'Bob';
case 'email':
return 'bob#bob.com';
}
}
);
$this->assertEquals('bob#bob.com', $mock->email);

Look in the mocked magic method __get. Probably you call there one more __get method from another and not properly mocked object.

What you are doing in the setUp method of your GetSnackTest class is incorrect.
If you want the code of the __get method to be executed (which would be the point of your test> I suppose), you have to change the way you call setMethods in the setup method.
Here's the complete explanation, but here's the relevant part:
Passing an array containing method names
The methods you have identified:
Are all stubs,
All return null by default,
Are easily overridable
So, you need to call setMethods by passing null, or by passing an array that contains some methods (the ones that you really want to stub), but not- __get (because you actually want the code of that method to be executed).
The, in the shouldReturnSnickers method, you will simply want to want to call $this->assertEquals('snickers', $this->sut->getSnack());, without the preceding lines with the expect part.
This will ensure the code of your __get method is actually executed and tested.

withAnyParameters() method can help you, this works correct:
$this->mock -> expects($this -> once())
-> method('__get') -> withAnyParameters()
-> will($this -> returnValue('snikers'));

Related

How to fix mockery mock object throwing BadMethodCallException?

In a silex application I have a KafkaAPiClient class which definitely has the public method postMessages.
<?php
namespace Kopernikus\KafkaWriter;
use Kopernikus\KafkaWriter\Model\AbstractMessage;
/**
* KafkaApiClient.
**/
class KafkaApiClient
{
/**
* #param AbstractMessage[] $msg
*/
public function postMessages(array $messages)
{
foreach ($messages as $message) {
$this->postMessage($message);
}
}
public function postMessage(AbstractMessage $msg)
{
...
}
}
I can call KafkaAPiClient::postMessages just fine, yet when mocking the class in a test:
<?php
namespace unit\Request;
use Kopernikus\KafkaWriter\KafkaApiClient;
/**
* MockeryMethodsNotBeingCallableTest
**/
class MockeryMethodsNotBeingCallableTest extends \PHPUnit_Framework_TestCase
{
public function testMockMethodIsCallable()
{
$leMock = \Mockery::mock(KafkaApiClient::class);
$leMock->postMessages([]);
}
}
I am getting:
1) unit\Request\MockeryMethodsNotBeingCallableTest::testMockMethodIsCallable
BadMethodCallException: Method Mockery_11_Kopernikus_KafkaWriter_KafkaApiClient::postMessages() does not exist on this mock object
~/le-project/tests/unit/Request/MockeryMethodsNotBeingCallableTest.php:14
I am confused, I was expecting for the mock to not do anything yet allow the methods to be called so that I later could add my expectations on it.
Though I have found a solution, I am still wondering if it is possible to mock all the methods by default, and later check if certain ones have been called.
There exists shouldIgnoreMissing method on the mock object. Calling that does exactly what it says on the tin, that is: ignoring calls to not yet defined methods, resulting in a mock that does nothing:
$leMock = \Mockery::mock(KafkaApiClient::class);
$leMock->shouldIgnoreMissing()
$leMock->postMessages([]);
And by nothing, it means nothing. I got into an other error for my queue when I instantiated the mock that way, as methods will return null by default and their return value has to be explicitly stated.
$msg = new Message('dummy-message');
$this->kafkaQueue
->shouldIgnoreMissing()
->shouldReceive('getMessages')->andReturn([$msg]);
Any call to getMessages will now return exactly the array [$msg].
Alternatively, one can be very explicit about what methods are called with Mockery, by adding shouldReceive:
public function testMockMethodIsCallable()
{
$leMock = \Mockery::mock(KafkaApiClient::class);
$leMock->shouldReceive('postMessages');
$leMock->postMessages([]);
}

Decouple or mock?

Suppose I have this class:
class SomeClass
{
// Top level function
public function execute($command)
{
// Get output from system tool
$output = $this->runTool($command);
// Check output for errors
if ($this->hasError($output))
return false;
// And parse success response from tool
return $this->parseOutput($output);
}
// There we're make a call to system
private function runTool($command)
{
return `/some/system/tool $command`;
}
[...]
}
I do not want to run system tool in my test, I want to replace a system call with predefined output.
So, the question is - should I create another class, move system call in it and mock that class in the test, or I can mock only that function of class which I will test?
Sure, both approaches will work, but which of them will be serve testing purposes better?
If you follow the single responsibility principle, you won't have this problem. Your class does not need to know how system calls are made, so you will have to use another class. You mock that.
IMO, in most cases when you need to mock protected or private methods, they do stuff that should be into another class and be mocked.
I would say it really depends on your infrastructure. Sometimes it is better to use Mock, sometimes Stub.
If the case is, that the class you want to test contains this unwanted method - use Mock and mock only this one function. That will make you sure, that any changes made to that class will be handled by the test.
If the unwanted function is a part of i.e. injected service or another class, which is not the domain of this particular test, you can create a stub.
You can't test private method, you can use a workaround and invoke it via reflection as described in this article and discussed in this SO QUESTION
But i suggest you to change the method visibility to protected and mock only the behaviour of the runTool method.
As example, suppose the following modified version of your class (i don't know how other method work so i suppose that you want to test their behaviour and take this implementation as example):
<?php
namespace Acme\DemoBundle\Service;
class SomeClass
{
// Top level function
public function execute($command)
{
// Get output from system tool
$output = $this->runTool($command);
// Check output for errors
if ($this->hasError($output))
return false;
// And parse success response from tool
return $this->parseOutput($output);
}
// There we're make a call to system
protected function runTool($command)
{
return `/some/system/tool $command`;
}
private function hasError($output)
{
return $output == "error";
}
private function parseOutput($output)
{
return json_decode($output);
}
}
As suppose the following test case:
<?php
namespace Acme\DemoBundle\Tests;
class SomeClassTest extends \PHPUnit_Framework_TestCase {
public function testCommandReturnError()
{
$mock = $this->getMockBuilder('Acme\DemoBundle\Service\SomeClass')
->setMethods(array('runTool'))
->disableOriginalConstructor()
->getMock()
;
$mock
->expects($this->exactly(1))
->method('runTool')
->with("commandName")
->will($this->returnValue("error"));
$this->assertFalse($mock->execute("commandName"));
}
public function testCommandReturnCorrectValue()
{
$mock = $this->getMockBuilder('Acme\DemoBundle\Service\SomeClass')
->setMethods(array('runTool'))
->disableOriginalConstructor()
->getMock()
;
$mock
->expects($this->exactly(1))
->method('runTool')
->with("commandName")
->will($this->returnValue('{"title":"myTitle"}'));
$returnValue = $mock->execute("commandName");
$this->assertEquals("myTitle", $returnValue->title);
}
}
Hope this help

Phpunit, how to test if method does "nothing"?

class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
}
}
so its easy to test if it threw exception
/**
* #expectedException Exception
*/
public function test()
{
new Testme(1);
}
but what if it didn't do anything?
public function test()
{
new Testme(2);
?? ? ? ? ?
}
Scenarios
You have two possible scenarios for a function to do nothing:
Scenario 1: No return statement
Your function does nothing because you do not perform actions in it and you do not include the return keyword in it:
public function doNothing()
{
// Do nothing.
}
Scenario 2: With return statement
Your function does nothing because you do not perform actions in it and you do include the return keyword in it without expressing any return value:
public function doNothing()
{
// Do nothing.
return;
}
Other scenarios
I will leave out of the cases to treat the following scenarios:
Case in which you do not return anything but you perform significant actions that can be tested on other objects. In this case you must unit-test the resulting states of the modified objects.
Case in which you do nothing but return something, then you should unit-test the return value.
Exploring the documentation in the PHP manual
For the first case, the PHP manual documents that the evaluated expression of the function will be null. It says here: http://php.net/manual/en/functions.returning-values.php in a note:
If the return is omitted the value NULL will be returned.
For the second case, the PHP manual documents that the evaluated expression of the funcion will also be null. It says here: http://php.net/manual/en/function.return.php in a note:
If no parameter is supplied, then the parentheses must be omitted and NULL will be returned. [...]
Conclusion
It is therefore clearly documented that a function that "does nothing" necessarily evaluates to null.
How to test a function that does nothing
Just assert your expectations:
$this->assertNull( $sut->doNothing() );
This way you "exercise" your function, you run over it making the code-coverage complete all the lines, and you "expect" that "nothing happened" by testing the null value of its evaluation as an expression, as documented.
How to test a constructor that does nothing
Nevertheless to test a constructor... well... common sense: What's the purpose of a constructor? Create an object (instance) of a certain type (class), right?
So... I prefer to start the 100% of my unit tests by checking that the $sut has been created. This is the VERY first test I write when I'm writing the code of a new class. This is the test I write even before the class exists. At the end, this is what the constructor is for. Red bar. Then I create the class. Green bar.
Let's say I have an Email class that takes a string and will be only created if a valid email is passed and throws exception otherwise. this is very similar to your question. A constructor that just "allows the creation" or "denies it by exploding the system".
I usually would do something like this:
//-------------------------------------------------//
// Tests //
//-------------------------------------------------//
/** #dataProvider validEmailProvider **/
public function testCreationIsOfProperClass( string $email )
{
$sut = $this->getSut( $validEmail );
$this->assertInstanceOf( Email::class, $sut );
}
/** #dataProvider invalidEmailProvider **/
public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
{
$this->expectException( EmailException::class );
$this->getSut( $invalidEmail );
}
//-------------------------------------------------//
// Data providers //
//-------------------------------------------------//
public function validEmailProvider() : array
{
return
[
[ 'alice#example.com' ],
[ 'bob.with-several+symbols#subdomain.another.subdomain.example.verylongTLD' ],
]
}
public function invalidEmailProvider() : array
{
return
[
[ 'missing_at_symbol' ],
[ 'charlie#cannotBeOnlyTld' ],
]
}
//-------------------------------------------------//
// Sut creators //
//-------------------------------------------------//
private function getSut( string $email ) : Email
{
return new Email( $email );
}
As I use PHP 7.0 and I put types everywhere, both entering the parameters and also in the return types, if the created object was not an Email, the getSut() function would fail first.
But even if I wrote it omitting the return type, the test tests what it is expected to happen: new Email( 'valid#example.com' ); is itself an expression that shoud evaluate to "something" of class Email::class.
How to test a constructor that does something
Code smell. The constructor probably should not do work. If any, just store parameters. If the constructor "does work" other than storing parameters consider lazy-processing on getters, or delegating that work in a factory or so.
How to test a constructor that "does nothing but store parameters"
Just like before + then get the data.
Test in your first test that the creation is an instance of something.
Then in another different test, exercise something like a getter that gets you what entered in the constructor even if the constructor did not anything (other than storing it).
Hope that this helps.
In PHPUnit 7.2+ you can also use TestCase::expectNotToPerformAssertions()
public function test()
{
// ...
$this->expectNotToPerformAssertions();
}
This has the same behaviour as the #doesNotPerformAssertions annotation.
2018+
Nowadays the best practice is annotation exactly for these cases:
/**
* #doesNotPerformAssertions
*/
public function testSomething()
{
$someService = new SomeObject();
$someService->shallNotFail();
}
Example pull-request
PHPUnit documentation (poor)
It's not possible. Add return statement and assert the result.
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
return true;
}
}
and then
$object = new Testme();
$this->assertTrue($object->testMe(2));
Note: The credits for this solution go to this related answer. The context may seem a little different, but the solution / workaround works the same way. Testing that an exception is not thrown is just the same as testing a method with no return value.
According to this issue thread, there is no built in solution for testing something like DoesNotThrowException in PHPUnit (yet).
So yes, one solution would be to return some dummy value from your method, like
public function testMe ($a)
{
if ($a == 1) { throw new Exception ('YAY'); }
return true;
}
and then assert it in your test. But if you don't want to change the code just for the test, you can work around it:
public function testExceptionIsNotThrown()
{
try {
new Testme(2);
}
catch(Exception $e) {
/* An exception was thrown unexpectedly, so fail the test */
$this->fail();
}
/* No exception was thrown, so just make a dummy assertion to pass the test */
$this->assertTrue(true);
}
It may seem hacky and not very intuitive, but if it's stupid but it works, it's not stupid.
This is an very interesting question, although lot of answers were written, none of them seems to properly answer the question, since you have asked using the class let me explain this way.
Please keep in mind that an instance method you have created in class should have only 2 intentions.
It can alter the state of a class ( change the class properties like private variables )
It returns the state of the class ( getters )
any thing other than this is meaningless unless it is a static method. for example
if you have class like this
class Foo {
private $prop = null;
public function fooMethod() {
$this->prop = "string";
}
public function getProp() {
return $this->prop;
}
}
the method fooMethod() does not return any thing, but it affects the state of $prop property in the class, you can test the method by
$this->assertNotNull( $instance->getProp() );
because you knew if this method is run then the prop $prop should be affected and state of that variable is changed.
Miscellanous Scenario: My method doesn't alter the state and also won't return any state variables.
Then the method is static. It should not be an instance method, and the static methods usually have return type, because they cant affect the state of the class and also can't return state variables. This constraints the static methods from storing a result somewhere (unless you store them globals, don't do that ), so it should definitely return some output. If you don't want to return output, then you could consider returning a boolean from static method.
public function testThrowingException()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('YAY');
(new Testme())->testMe(1);
}
public function testNotThrowingException()
{
$this->expectNotToPerformAssertions();
(new Testme())->testMe(2);
}
I stumled upon the same problem. To ensure "nothing" has happened it's enough to just call you the method in your unit test. If it fails the test will fail anyway.
If you just call your method without the #expectedException annotation like this
public function test()
{
new Testme(1);
}
you'll get an error
There was 1 error:
1) Testme::testMe
Exception: YAY

Mocking public method of testing object

I have a class that has two public method. It looks something like following:
class IntRequest
{
public function updateStatus()
{
$isValid = $this->checkValidity();
// ... next is a complex logic that use $isValid
}
/**
* #return bool
*/
public function isValid()
{
// another complex logic
}
}
I need to test a first function - IntRequesr::updateStatus; however I need to run to tests. The first one with IntRequests::isValid returns false and the second one with true as a result of IntRequests::isValid
I try to mock that function but tests run with calling actual IntRequests::isValid not mocked one.
My testing code is
$intRequest = new IntRequests;
$mock = m::mock($intRequest);
$mock->shouldReceive('isValid')
->once()
->andReturn(true);
$res = $mock->updateStatus();
$this->assertTrue($res);
I've try to call $res = $intRequest->updateStatus() instead of $res = $mock->updateStatus() but with no luck.
So, I am wondering is it possible to mock function that is called inside testing method?
You need a partial mock (a mock object, in which some of the methods are stubbed, while the rest are left as is). Since I've done such only with the phpunit's own mock library, I can only point you to the documentation, but it seems that you should just add ->makePartial() to your mock instantiation

PHPunit mock only observing

I'm having some problems understanding mock objects.
What I want is an observer that works normally but do make sure the methods are called with the right parameters.
from what I understand so far this should be what I'm looking for:
Observer:
class Observer
{
public function returnFooIfBar($bar)
{
return ($bar == 'bar') ? 'foo' : ;
}
}
Subject:
class Subject
{
$obs;
__construct(Observer $dependency)
{
$this->obs = $dependency;
}
public function tradeStrings($string)
{
$this->obs->returnFooIfBar($string);
}
}
Test:
class SubjectTest
{
public function testCallsObsMethod()
{
$obs = $this->getMock('Observer') ;
$obs->expect($this->once())
->method('returnFooIfBar')
->with($this->equlTo('bar')) ;
$subj = new Subject($obs);
$returnString= $subj->TradeStrings('bar') ;
$this->assertEqual('foo', $returnString) ;
}
}
From what I understand this tests that:
Observer::getFooIfBar is called once.
Observer::getFooIfBar got the string 'bar'
3.The method works as defined in the class and return 'foo' as a string.
As I understand no functionality of the original class is changed, other than the constructor / autoloading not being run.
If I mock a method when running getMock() the mocked object's method will only return something if I specify it.
$obs = $this->getMock('Observer', array('returnFooIfBar'));
$obs->expects($this->once())
->method('returnFooIfBar')
->with('bar')
->will($this->returnValue('foo');
Do I understand this right? if not could you please clarify for me as I would love some clarity on this. :)
Edit: Changed the post to make it clearer what I am after and how I currently understand it.
If you let phpunit create a mocked object, it internally builds up a new temporary class which extends the original one and implements all methods of this class with mock specific code.
The idea behind this is to decouple objects in your test cases. while your given example is valid, you would not use it this way.
but your example test would fail anyway, because the mocked function returnStringFooIfBar would not return anything.
this is how it should work:
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnValue('foo'));
$returnString= $obs->returnStringFooIfBar ('bar') ;
$this->assertEqual('foo', $returnString) ;
but a real world test case would involve some object to test:
class TestObject {
private $observer;
public function __construct($observer) {
$this->observer = $observer;
}
public function doMagicAndNotify() {
// do heavy magic
//notify observer
$obsresult = $this->observer->returnStringFooIfBar('bar');
return 'didmagic';
}
}
class TestObjectTest extends PHPUnit_Framework_TestCase {
public function testObserverCalling() {
$obs = $this->getMock('observer') ;
$obs->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'));
$test = new TestObject($obs);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
}
EDIT:
What I want is an observer that works normally but do make sure the methods are called with the right parameters.
As I understand no functionality of the original class is changed,
other than the constructor / autoloading not being run.
It is actually the other way around. The temporary child class of Observer overwrites all (or specified) methods and changes the original functionality (the parent of mocked methods is simply not executed). it does not overwrite the constructor, its called anyway.
It is not possible to assert a method call of a mocked method and call its original method at the same sime.
See Method Template and the Generator for reference.
And please remember: you are not testing the correct behaviour of your observer here, your are mocking its beheviour in order to test the subject.
sitenote: $this->returnCallback($someCallback) is a mighty function and might help you.
i don't like the idea, but you could do something like this:
public function testObserverCalling() {
$obs = new Observer();
$obsmock = $this->getMock('observer') ;
$obsmock->expect($this->once())
->method('returnStringFooIfBar')
->with($this->equlTo('bar'))
->will($this->returnCallback(array($obs, 'returnStringFooIfBar')));
$test = new TestObject($obsmock);
$returnString= $test->doMagicAndNotify() ;
$this->assertEqual('didmagic', $returnString) ;
}
You can do this now in phpunit with enableProxyingToOriginalMethods().
class SubjectTest extends \PHPUnit\Framework\TestCase
{
public function testCallsObsMethod()
{
$obs = $this->getMockBuilder('Observer')
->enableProxyingToOriginalMethods()
->getMock();
$obs->expect($this->once())
->method('returnFooIfBar');
$subj = new Subject($obs);
$returnString= $subj->tradeStrings('bar') ;
$this->assertEqual('foo', $returnString) ;
}
}

Categories