I'm trying to create my first phpunit test and find myself needing to stub a method on an IMailer interface.
interface IMailer
{
public function send($to, $from, $cc, $subject, $body);
public function sent();
}
$mailer = $this->getMockBuilder(
'IMailer',
array('send', 'sent'))->getMock();
$mailer->method('send')->willRreturn(0);
However, I keep getting
PHP Fatal error:
Call to undefined method Mock_Mailer_13fc0a04::method()
in ...Test.php on line 16
a var_dump($mailer); results in
class Mock_IMailer_4c3e02a7#215 (1) {
private $__phpunit_invocationMocker =>
NULL
}
Working with the expect($this->any()) gives a dito error - it seems that the mocked object does not have any mock functionality...
I'm running phpunit 3.7.28, and php 5.5.9, on an ubuntu box.
How come? How can I fix it?
The getMockBuilder function accepts only the className as parameter. The correct way to initialize your mock object methods would be to use setMethods function (see phpunit docs)
$mailer = $this->getMockBuilder('IMailer')
->setMethods(array('send', 'sent'))
->getMock();
Additionally you probably want to have some expects definition also when you use your mock object:
$mailer->expects($this->any())
->method('send')
->willReturn(0);
EDIT
The above holds true for newer phpunit versions. For phpunit 3.7.28 the mock object usage is a bit different (i.e. the expects seems to be mandatory and willReturn is not yet available). For 3.7.28 version you should modify the second part to:
$mailer->expects($this->any())
->method('send')
->will($this->returnValue(0));
I would recommend updating to later phpunit version as it seems to be somewhat difficult to find documentation to this much older releases.
An alternative solution, for anybody that is still using old versions of PHPUnit, but still wants to be able to call method() directly, is to override the default mock object class template.
Copy MockObject/Generator/mocked_class.tpl.dist, and name the copy mocked_class.tpl. Then, just add the code for the method() method to the template:
public function method()
{
$any = new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
$expects = $this->expects($any);
$args = func_get_args();
return call_user_func_array(array($expects, 'method'), $args);
}
This will allow you to call $mock->method() directly. However, you need to still use ->will($this->returnValue(0)) instead of ->willReturn(0). To do that, you need to introduce a custom invocation builder and invocation mocker:
class My_MockObject_Builder_InvocationMocker
extends PHPUnit_Framework_MockObject_Builder_InvocationMocker {
public function willReturn( $value ) {
return $this->will( new PHPUnit_Framework_MockObject_Stub_Return( $value ) );
}
}
class My_MockObject_InvocationMocker
extends PHPUnit_Framework_MockObject_InvocationMocker {
public function expects( PHPUnit_Framework_MockObject_Matcher_Invocation $matcher ) {
return new My_MockObject_Builder_InvocationMocker($this, $matcher);
}
}
And update your template again, to use My_MockObject_InvocationMocker instead of PHPUnit_Framework_MockObject_InvocationMocker.
The full template would then look like this:
{prologue}{class_declaration}
{
protected static $staticInvocationMocker;
protected $invocationMocker;
{clone}{mocked_methods}
public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
{
return $this->__phpunit_getInvocationMocker()->expects($matcher);
}
public function method()
{
$any = new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
$expects = $this->expects($any);
$args = func_get_args();
return call_user_func_array(array($expects, 'method'), $args );
}
public static function staticExpects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
{
return self::__phpunit_getStaticInvocationMocker()->expects($matcher);
}
public function __phpunit_getInvocationMocker()
{
if ($this->invocationMocker === NULL) {
$this->invocationMocker = new My_MockObject_InvocationMocker;
}
return $this->invocationMocker;
}
public static function __phpunit_getStaticInvocationMocker()
{
if (self::$staticInvocationMocker === NULL) {
self::$staticInvocationMocker = new My_MockObject_InvocationMocker;
}
return self::$staticInvocationMocker;
}
public function __phpunit_hasMatchers()
{
return self::__phpunit_getStaticInvocationMocker()->hasMatchers() ||
$this->__phpunit_getInvocationMocker()->hasMatchers();
}
public function __phpunit_verify()
{
self::__phpunit_getStaticInvocationMocker()->verify();
$this->__phpunit_getInvocationMocker()->verify();
}
public function __phpunit_cleanup()
{
self::$staticInvocationMocker = NULL;
$this->invocationMocker = NULL;
}
}{epilogue}
Related
In the below example, I want to mock the calling of getBaseValue() inside the multipliedValue(). But I cannot figure it out.
class Sample
{
function multipliedValue()
{
$value = $this->getBaseValue();
return $value * 2;
}
function getBaseValue()
{
return 2;
}
}
I have used PHPUnit mocking, but it didn't work. So, I used the following code:
class SampleTest extends PHPUnit_Framework_TestCase
{
function testMultipliedValueIfBaseValueIsFalse()
{
$mockedObject = $this->getMockBuilder(Sample::class)
->setMethods(['multipliedValue', 'getBaseValue'])
->getMock();
$mockedObject->expects($this->any())
->method("getBaseValue")
->willReturn(false);
$result = $mockedObject->multipliedValue();
$this->assertFalse($result);
}
}
I tried to create a global function, but only force one of the method to return my desired value, the rest just go as they are. How should I approach this test?
The error I am currently getting is for the $this in the multipliedValue() method, which treats it as the stubbed object.
All the methods listed in the ->setMethods() will be stubbed and return null by default so if you only want to stub getBaseValue then do:
$mockedObject = $this->getMockBuilder(Sample::class)
->setMethods(['getBaseValue'])
->getMock();
$mockedObject->expects($this->any())
->method("getBaseValue")
->willReturn(false);
$result = $mockedObject->multipliedValue();
$this->assertFalse($result);
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
Note:all code working fine without phpunit
file 1:common.php:
public function setNIMUID( $NIMUID ) {
if(is_bool(Cache::get("$NIMUID"))) {
$user_Array=array("_JID"=>(string)$NIMUID);
Cache::set("$NIMUID",$user_Array);
}
$this->NIMUID=(string)$NIMUID ;
}
File 2 :memcache.class.php
method 1:
protected function __construct(array $servers) {
if(!$servers) {
trigger_error('No memcache servers to connect', E_USER_WARNING);
}
for($i = 0, $n = count($servers); $i<$n; ++ $i) {
($con = memcache_connect(key($servers[$i]), current($servers[$i])))&&$this->mc_servers[] = $con;
}
$this->mc_servers_count = count($this->mc_servers);
if(!$this->mc_servers_count) {
$this->mc_servers[0] = null;
}
}
method 2:
static function get($key) {
return self::singleton()->getMemcacheLink($key)->get($key);
}
method 3:
static function singleton() {
//Write here where from to get the servers list from, like
global $memcache_servers;
self::$instance||self::$instance = new Cache($memcache_servers);
return self::$instance;
}
File 3 : commonTest.php
public function testCommon()
{
$Common = new Common();
$Common->setNIMUID("saurabh4");
}
$memcache_servers variable :
$memcache_servers = array(
array('localhost'=>'11211'),
array('127.0.0.1'=>'11211')
);
Error :
Fatal error: Call to undefined function memcache_connect()
Unit tests should be repeatable, fast and isolated. That means that you shouldn't connect to external services to unit test your classes.
If you want to test that Common is working fine, you should test its behaviour, which in this case is that is calling the Cache class as you'd expect.
For that, you'll need to use mocks. With mocks you can set some expectations, like that the object will be called in a specific manner. If your class is called the memcached class as expected, you can assume your functionality is working fine. How do you know the Cache class is working fine? Because the Cache class would have its own unit test.
In order to use mocks (or stubs), you need to change the way you program and avoid static calles like the one in Cache::set(). Instead, you should use class instances and normal calls. How? Passing the Cache instance to your Common class. This concept is called Dependency injection. Your Common code would look like this:
public function __construct( $cache ) {
$this->cache = $cache;
}
public function setNIMUID( $NIMUID ) {
if(is_bool($this->cache->get("$NIMUID"))) {
$user_Array=array("_JID"=>(string)$NIMUID);
$this->cache->set("$NIMUID",$user_Array);
}
$this->NIMUID=(string)$NIMUID ;
}
I try to extend the CheckfrontAPI class with my new class.
In my case I use the Singleton pattern in order to load only one instance at a time of my class and I get that error
Fatal error: Declaration of CheckFrontIntegrator::store() must be compatible with that of CheckfrontAPI::store() in /home/my_web_site/public_html/wp-content/plugins/checkfront/class/Checkfront_Integration.php on line 83
Any idea on how to solve that issue ?
Here is the CheckfrontAPI source code : https://github.com/Checkfront/PHP-SDK/blob/master/lib/CheckfrontAPI.php
And here is my class that extends that class:
<?php
class CheckFrontIntegrator extends CheckfrontAPI
{
private static $instance = null;
public $tmp_file = '.checkfront_oauth';
final protected function store($data = array())
{
$tmp_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR. $this->tmp_file;
if(count($data))
{
file_put_contents(
$tmp_file,
json_encode(
$data,
true
)
);
}
elseif(is_file($tmp_file))
{
$data = json_decode(
trim(
file_get_contents(
$tmp_file
)
),
true
);
}
return $data;
}
public function session($session_id, $data = array())
{
$_SESSION['checkfront']['session_id'] = $session_id;
}
public static function instance($data)
{
if(!isset(self::$instance))
{
self::$instance = new CheckFrontIntegrator($data);
}
return self::$instance;
}
public function __construct($data)
{
if(session_id() == '')
{
session_start();
}
parent::__construct($data, session_id());
}
}
?>
And I initiate the new instance of that class like that:
$this->checkfront_integrator = CheckFrontIntegrator::instance($args);
where args are all the important information needit by the class to initiate a new object
AFTER EDIT
I have change my method store from:
final protected function store($data = array())
....
to
protected function store($data)
....
and the problem still occure :(
CheckfrontAPI is an abstract class? in this case your CheckFrontIntegrator::store() arguments count must be identical to original declaration
EDIT
I see on github
abstract protected function store($data);
your override must be:
protected function store($data) {
}
You are extending CheckfrontAPI. CheckfrontAPI has a method store(). If you override that method you must do it properly.
Post the code of CheckfrontAPI and your class Checkfront_Integration: when can understand what's the problem.
When you want to extent the functionality of an existing class by writing your own class and the class you are extending is is an abstract one, you'll need to make sure that the function calls are compatible.
What does this mean?
If the class you are extending has this function call for example :
function walk($direction, $speed = null);
Then you will have to honor the function signature in your implementation - that means you'll still have to have to pass two function arguments in your version.
You will not be able to alter is to be like this :
function walk($direction, $speed, $clothing);
I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.
Example:
class urlDisplayer {
private $storage;
public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
public function displayUrl($name) {}
public function displayLatestUrls($count) {}
}
interface IUrlStorage {
public function addUrl($name, $url);
public function getUrl($name);
}
class MysqlUrlStorage implements IUrlStorage {
// saves and retrieves from database
}
class NonPersistentStorage implements IUrlStorage {
// just stores for this request
}
Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?
Edit: example test:
public function testUrlDisplayerDisplaysLatestUrls {
// get mock storage and have it return latest x urls so I can test whether
// UrlDisplayer really shows the latest x
}
In this test the mock should return a number of urls, however in the documentation I only how to return one value.
Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?
PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.
In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.
public function setUp()
{
parent::setUp();
$this->fixture = new UrlDisplayer(); //change this to however you create your object
//Create a list of expected URLs for testing across all test cases
$this->expectedUrls = array(
'key1' => 'http://www.example.com/url1/'
, 'key2' => 'http://www.example.net/url2/'
, 'key3' => 'http://www.example.com/url3/'
);
}
public function testUrlDisplayerDisplaysLatestUrls {
//Init
$mockStorage = $this->getMock('IUrlStorage');
$mockStorage->expects($this->any())
->method('getUrl')
->will( $this->returnCallback(array($this, 'mockgetUrl')) );
reset($this->expectedUrls); //reset array before testing
//Actual Tests
$this->assertGreaterThan(0, count($this->expectedUrls));
foreach ( $this->expectedUrls as $key => $expected ) {
$actual = $this->fixture->displayUrl($key);
$this->assertEquals($expected, $actual);
}
}
public function mockGetUrl($name)
{
$value = current($this->expectedUrls);
next($this->expectedUrls);
//Return null instead of false when end of array is reached
return ($value === false) ? null : $value;
}
Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.
In this specific case, I would suggest using the below class instead:
class MockStorage implements IUrlStorage
{
protected $urls = array();
public function addUrl($name, $url)
{
$this->urls[$name] = $url;
}
public function getUrl($name)
{
if ( isset($this->urls[$name]) ) {
return $this->urls[$name];
}
return null;
}
}
?>
Then in your unit test class you simply instantiate your fixture like below:
public function setUp() {
$mockStorage = new MockStorage();
//Add as many expected URLs you want to test for
$mockStorage->addUrl('name1', 'http://example.com');
//etc...
$this->fixture = new UrlDisplayer($mockStorage);
}