Mocking public method of testing object - php

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

Related

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.

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

Create a mock class whose methods return values when instantiated

How can I create a mock class (not just a mock object), with a method that, when instantiated will return a predictable value?
In the code below, I am testing a larger concept (accounts->preauthorize()), but I need to mock the object Lookup so that I can get predictable results for my test.
I'm using PHPUnit and CakePHP, if that matters. Here is my situation:
// The system under test
class Accounts
{
public function preauthorize()
{
$obj = new Lookup();
$result = $obj->get();
echo $result; // expect to see 'abc'
// more work done here
}
}
// The test file, ideas borrowed from question [13389449][1]
class AccountsTest
{
$foo = $this->getMockBuilder('nonexistent')
->setMockClassName('Lookup')
->setMethods(array('get'))
->getMock();
// There is now a mock Lookup class with the method get()
// However, when my code creates an instance of Lookup and calls get(),
// it returns NULL. It should return 'abc' instead.
// I expected this to make my instances return 'abc', but it doesn't.
$foo->expects($this->any())
->method('get')
->will($this->returnValue('abc'));
// Now run the test on Accounts->preauthorize()
}
You have several problems here, but the main one is that you are instantiating your Lookup class in the method that requires it. This makes it impossible to mock. You need to pass an instance of Lookup into this method to decouple the dependency.
class Accounts
{
public function preauthorize(Lookup $obj)
{
$result = $obj->get();
return $result; // You have to return something here, you can't test echo
}
}
Now you can mock Lookup.
class AccountsTest
{
$testLookup = $this->getMockBuilder('Lookup')
->getMock();
$testLookup->expects($this->any())
->method('get')
->will($this->returnValue('abc'));
$testAccounts = new Accounts();
$this->assertEquals($testAccounts->preauthorize($testLookup), 'abc');
}
Unfortunately, I can't test this test, but this should get you moving in the right direction.
Obviously, a unit test for the Lookup class should also exist.
You may also find my answer here of some use.

How to test Guzzle event in PHPUnit

I am having trouble understanding how I should go about unit testing a method which utilizes \Guzzle\Common\Event and has no return. I have a function
public function setRequest(\Guzzle\Common\Event $e) {
$e['request']->getQuery()->set('key', $this->getKey());
}
I cannot get the methods described at Guzzles mock object documentation to produce a successful test. What all needs to be mocked for this to work? getQuery() is part of the \Guzzle\Http\Message\Request I guess? What about the set()?
Edit: What I did so far is this, but I don't know if this is the correct approach. It succeeds, but it does not assert any test. I don't know that I can if the method is not returning anything.
public function testSetRequest()
{
$collection = $this->getMock('Guzzle\\Common\\Collection');
$collection->expects($this->once())
->method('set')
->will($this->returnValue(array('key' => 321)));
$request = $this->getMockBuilder('Guzzle\\Http\\Message\\Request')
->disableOriginalConstructor()
->getMock();
$request->expects($this->once())
->method('getQuery')
->will($this->returnValue($collection));
$event = $this->getMock('Guzzle\\Common\\Event');
$event->expects($this->once())
->method('offsetGet')
->with('request')
->will($this->returnValue($request));
$result = $this->place->setRequest($event);
}
I tracked set() down to the guzzle common collection. btw, $this->place simply refers to the instance of the object being tested, set in the setUp() function.

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

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

Categories