Cannot mock partial Log facade in Laravel - php

I'm trying to mock laravel Log. This is my code:
public function test_process_verify_card()
{
Log::shouldReceive('error')->once();
Log::makePartial();
$class = new MyClass();
$class->myFunction();
}
This is MyClass look like:
class MyClass
{
public function __construct()
{
$this->logger = Logg::channel('test');
}
public function myFunction()
{
// ... some logic
$this->loggger->error('Errror!!');
}
}
When I run test this test case, it throw error
Call to a member function runningUnitTests() on null
at vendor/laravel/framework/src/Illuminate/Log/LogManager.php:568
I tried to debug this error by putting dd() in LogManager class
protected function parseDriver($driver)
{
$driver ??= $this->getDefaultDriver();
dd($this->app); // <--- This is my code
if ($this->app->runningUnitTests()) {
$driver ??= 'null';
}
return $driver;
}
But it show that $this->app is not null.
I've tried mock facade Date before and it works fine.
I want to test that myFunction executes logging action. Is this correct way to do it?
Update
I also tried to mock it through partialMock() function:
public function test_process_verify_card()
{
$this->partialMock(Logger::class, function (MockInterface $mock) {
$mock->shouldReceive('error')->once();
});
$class = new MyClass();
$class->myFunction();
}
But it still not works, it shows error:
Method error(<Any Arguments>) from Mockery_0_Illuminate_Log_Logger should be called
exactly 1 times but called 0 times.
at vendor/phpunit/phpunit/phpunit:98

I would believe the problem why this is not working, is as Log::channel returns a channel on the partial mock. Therefor the mocked instance never receive the error call.
In Mockery you can easily do chained calls, by using '->' in the shouldReceive() call.
Log::shouldReceive('channel->error')
->once()
->andReturn(null);

Related

PHPUnit: How to mock an internally called function by the method under test

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);

Mockery not able to call my method in testing method

I'm trying to write a test for a method in the class below. However, when I run the test I get the error that get_b64 is never run? I don't see how this is not running.
I've had a little look into the mockery documentation for testing static methods, but as far as I can tell this error isn't due to that?
What do I need to change with my testing strategy or be able to mock the function call in the mocked object?
Class:
namespace App\Services\Steam;
use App\Services\Steam\Utils;
class Steam
{
public function profile(string $steamID)
{
$b64 = Utils::get_b64($steamID);
if ($b64 === null) {
throw new \App\Exceptions\InvalidSteamId();
}
return new Profile($b64);
}
}
TestCase:
public function test_create_user_object()
{
$id = "123"
$utilsMock = Mockery::mock(\App\Services\Steam\Utils::class);
$utilsMock->shouldReceive('get_b64')
->once()
->with($id)
->andReturn($id);
$steam = new \App\Services\Steam\Steam();
$steam->profile($id);
}
You call get_b64 statically, which means it is called from the class, not an object.
To mock such calls you need to use aliases:
public function test_create_user_object()
{
$id = "123"
$utilsMock = Mockery::mock('alias:\App\Services\Steam\Utils');
$utilsMock->shouldReceive('get_b64')
->once()
->with($id)
->andReturn($id);
$steam = new \App\Services\Steam\Steam();
$steam->profile($id);
}
Bear in mind that it completely replaces the Utils class, so if you have more static functions called from the class, you need to mock them as well.

PHPUnit mocked method returns null

I am trying to test the below class using PHPUnit
class stripe extends paymentValidator {
public $apiKey;
public function __construct ($apiKey){
$this->apiKey = $apiKey;
}
public function charge($token) {
try {
return $this->requestStripe($token);
} catch(\Stripe\Error\Card $e) {
echo $e->getMessage();
return false;
}
}
public function requestStripe($token) {
// do something
}
}
My test scripts is like the below:
class paymentvalidatorTest extends PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function test_stripe() {
// Create a stub for the SomeClass class.
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe', 'charge'])
->getMock();
$stripe->expects($this->any())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
}
}
With my test script I was expecting the test double of stripe::charge() method will do exactly as the defined in the original class and the stripe::requestStripe() will return 'Miaw'. Therefore, $stripe->charge('token') should also return 'Miaw'. However, when I run the test I get:
Failed asserting that null matches expected 'Miaw'.
How should I fix this ?
Where you're calling setMethods, you're telling PHPUnit that the mock class should mock the behaviour of those methods:
->setMethods(['requestStripe', 'charge'])
In your case it looks like you want to partially mock the class, so that requestStripe() returns Miaw, but you want charge to run its original code - you should just remove charge from the mocked methods:
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe'])
->getMock();
$stripe->expects($this->once())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
While you're at it you may as well specify how many times you expect requestStripe() to be called - it's an extra assertion with no extra effort, as using $this->any() doesn't provide you with any added benefit. I've included using $this->once() in the example.

PHPUnit call to undefined method `Mock_x_::method()`

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}

Mocking method in abstract Controller with phpunit

I want to mock a method with PHPunit in Zend Framework 2.
The method is part of a 'AbstractController' class which is extended by the class I am testing.
The code I use to test:
public function testAddPostAction()
{
$this->mockBjy();
$this->mockZFC();
$zfcUser = $this->getMockBuilder('Common\Controller\AbstractController')->getMock();
$zfcUser ->method('getUserId')
->will($this->returnValue('2'));
$this->assertEquals(2, $zfcUser->getUserId());
$this->dispatch('/leaverequest/add','POST', array('user' => '2','reasonforleave' => 'PHPunit','leaveRequestDates'=> '05/06/2015 - 05/15/2015'));
$this->assertModuleName('Employment');
$this->assertControllerName('employment\controller\leaverequest');
$this->assertControllerClass('LeaveRequestController');
$this->assertActionName('add');
$this->assertMatchedRouteName('leaverequest/add');
}
The code in the abstract Controller:
protected function getUserId()
{
return $this->zfcUserAuthentication()
->getIdentity()
->getId();
}
The code in the controller being tested that requires this method, look at the 2nd parameter in the method being called on the second line of the if statement ($this->getUserId()).
public function addAction()
{
if ($this->getRequest()->isPost()) {
$this->getServiceLocator()->get('leaveRequestService')->addLeaveRequest($this->getRequest()->getPost()->toArray(), $this->getUserId());
$returnValue = $this->redirect()->toRoute('leaverequest/list');
} else {
$users = $this->getServiceLocator()->get('userService')->findAllUsers();
$returnValue = new ViewModel(array('users' => $users));
}
return $returnValue;
}
I would need the mock the result of this method, but am unable to do so with the current code. The message I receive is as follows: Call to a member function getId() on a non-object in .....
Thank you in advance :)
So I have realised that mocking the abstract was a bad approach and should rather mock the leave request service called by the dispatch method. I have now tried to mock the leave request addLeaveRequestService, but the second parameter $this->getUserId is still being triggered causing the unit test to go the the getId method in the abstract controller where it fails and provides the error message "
"call to member function on a non-object"

Categories