PHPUnit 9.5 set mock (or stub or fake) to the function that calling within the class - phpunit

I have a function to implement Unit test function. This function is calling a other function within the same class. I want to set pre-defined returns for this calling function (functwo()).
The project is using 'Zend_Test_PHPUnit' PHPUnit 9.5.20
I have a class like this:
class myClass {
public $x;
public $y;
public $z;
public function __constructor($x, $y, $z){
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
public function funcone()
{
$i = 1;
$a = $i + $this->functwo();
return $a;
}
public function functwo()
{
return 3;
}
}
And my Testing class is:
class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
...
}
class myClassTest extends ControllerTestCase
{
public function testCheck1()
{
$x = true;
$y = 10.5;
$z = [];
$mock = $this->getMockBuilder(myClass::class)
->setConstructorArgs([$x, $y, $z])
->getMock();
$mock->method('functwo')->willReturn(5);
$result = $mock->funcone();
$this->assertTrue($result === 6);
}
}
I still getting the null for $result and it seems testing is not calling funcone(). Any idea where I am got wrong?
There was 1 failure:
1) myClassTest::testCheck1
Failed asserting that false is true.

Related

Symfony / Fixtures "Reference already exists, use method setReference in order to override it"

I created 2 fixtures files and want to link them but got the error : Reference to "genre_reference" already exists, use method setReference in order to override it.
Here is my book fixtures :
class LivreFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
$faker = Factory::create('fr_FR');
$faker->seed(123);
for ($i = 0; $i < 10; $i++) {
$livre = new Livre();
$livre->setTitre($faker->sentence($nbWords = 4, $variableNbWords = true));
$livre->setAuteur($faker->name);
$livre->setDescription($faker->text);
$livre->setDateDeParution($faker->dateTime($format = 'Y-m-d'));
$livre->setGenre($this->getReference('genre_reference'));
$manager->persist($livre);
}
$manager->flush();
}
public function getDependencies()
{
return array(
GenreFixtures::class,
);
}
}
And my genre fixtures file :
class GenreFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
$faker = Factory::create('fr_FR');
$faker->seed(123);
for ($i=0; $i < 5; $i++) {
$genre = new Genre();
$genre->setName($faker->colorName);
$manager->persist($genre);
$this->addReference('genre_reference', $genre);
}
$manager->flush();
}
}
I don't really understand the setreference method, how to do it. Any help ?
I think you should add $i to your genre reference name.
$this->addReference('genre_reference_' . $i, $genre);
After that, you can use it by
$this->getReference('genre_reference_0');
//OR
$this->getReference('genre_reference_1');
//OR
$this->getReference('genre_reference_' . $i);

Phpunit testing callback functions when passing them to a stub method

Here is the method to test.
public function performPoolRequest(RecreationRequestsCollection $requests): RecreatedPaymentsPoolReading
{
$count_request = count($requests);
if($count_request) {
$pool_recreate_payments = new RecreatedPaymentsPool();
$this->http_client->sendPoolRequest(
$this->generateRequests($requests),
$count_request,
function (ResponseInterface $response, $index) use ($requests, $pool_recreate_payments) {
$this->successHandler($response, $requests, $index, $pool_recreate_payments);
},
function (BadResponseException $reason, $index) use ($requests, $pool_recreate_payments) {
$this->failureHandler($reason, $requests, $index, $pool_recreate_payments);
}
);
return $pool_recreate_payments;
} else {
throw new PoolRequestException('Incorrect amount of requests: ' . $count_request);
}
}
Difficulties arose with this piece of code:
$this->http_client->sendPoolRequest(
$this->generateRequests($requests),
$count_request,
function (ResponseInterface $response, $index) use ($requests, $pool_recreate_payments) {
$this->successHandler($response, $requests, $index, $pool_recreate_payments);
},
function (BadResponseException $reason, $index) use ($requests, $pool_recreate_payments) {
$this->failureHandler($reason, $requests, $index, $pool_recreate_payments);
}
);
I made a mock object $this->http_client
But I don't know how to test methods in the argumets ($this->successHandler, $this->failureHandler, this->generateRequests($requests)) that are sent to the method sendPoolRequest.
All of these methods are protected. I understand how to test them using a reflection object, but I want to know if there is an option to test them within a single test by checking the values in $ pool_recreate_payments.
You have to fake what the http client is doing in some way. That means: actually calling the callback functions. While it might be possible to do that with a PHPUnit mock object, writing own test doubles is often easier.
Here are a few examples to get you started.
public function testAllFailing()
{
$client = new class implements HttpClient {
public function sendPoolRequest(array $requests, int $numRequests, callable $successHandler, callable $errorHandler)
{
foreach ($requests as $index => $request) {
$errorHandler(new BadResponseException(/*...*/), $index);
}
}
};
$myService = new MyService($client);
$result = $myService->performPoolRequest(/*...*/);
self::assertEquals(/*...*/, $result);
}
public function testAllSucceeding()
{
$client = new class implements HttpClient {
public function sendPoolRequest(array $requests, int $numRequests, callable $successHandler, callable $errorHandler)
{
foreach ($requests as $index => $request) {
$successHandler(new Response(/*...*/), $index);
}
}
};
$myService = new MyService($client);
$result = $myService->performPoolRequest(/*...*/);
self::assertEquals(/*...*/, $result);
}
public function testEveryOtherFails()
{
$client = new class implements HttpClient {
public function sendPoolRequest(array $requests, int $numRequests, callable $successHandler, callable $errorHandler)
{
foreach ($requests as $index => $request) {
if ($index % 2 === 0) {
$successHandler(new Response(/*...*/), $index);
} else {
$errorHandler(new BadResponseException(/*...*/), $index);
}
}
}
};
$myService = new MyService($client);
$result = $myService->performPoolRequest(/*...*/);
self::assertEquals(/*...*/, $result);
}
Since Guzzle is used as the http client, I decided to use its capabilities in terms of creating stubs for the response object. Here is a link to the documentation Guzzle documentation. The solution turned out to be simple and allowed us to extensively test the business logic of the response.

phpunit must be traversable or implement interface Iterator

I'm trying to unit test my service, containing a dependency Finder Component of symfony2(http://symfony.com/doc/current/components/finder.html).
I'm getting :
[Exception] Objects returned by Mock_Finder_91776c5c::getIterator()
must be traversable or implement interface Iterator
The service :
public function getFile($fileName, $path = '/')
{
if($this->wrapper == null || $this->connection == null)
throw new LogicException("call method setFTP first");
// get file on ftp server
$this->connection->open();
$downloadComplete = $this->wrapper->get($this->tmpDir . $fileName, $path . $fileName);
$this->connection->close();
if($downloadComplete == false)
return false; // TODO exception ?
// return file downloaded
$this->finder->files()->in(__DIR__);
foreach ($this->finder as $file) {
return $file;
}
return false; // TODO exception ?
}
And the test
class FtpServiceTest extends \PHPUnit_Framework_TestCase
{
protected $connectionMock;
protected $ftpWrapperMock;
protected $finderMock;
protected function setUp()
{
$this->connectionMock = $this->getConnectionMock();
$this->ftpWrapperMock = $this->getFTPWrapperMock();
$this->finderMock = $this->getFinderMock();
}
protected function tearDown()
{
}
private function getFinderMock()
{
return $this->getMockBuilder(Finder::class)
->disableOriginalConstructor()
->getMock('Iterator');
}
private function getConnectionMock()
{
return $this->getMockBuilder(Connection::class)
->disableOriginalConstructor()
->getMock();
}
private function getFTPWrapperMock()
{
return $this->getMockBuilder(FTPWrapper::class)
->disableOriginalConstructor()
->getMock();
}
// tests
public function testMe()
{
// arrange
$host = 'localhost';
$user = 'user';
$password = '1234';
$filesArray = new ArrayObject(array(''));
$service = new FtpService('var/tmp/');
$service->setFTP($this->connectionMock, $this->ftpWrapperMock);
$service->setFinder($this->finderMock);
$this->connectionMock
->expects($this->once())
->method('open');
$this->ftpWrapperMock
->expects($this->once())
->method('get')
->will($this->returnValue(true));
$this->connectionMock
->expects($this->once())
->method('close');
$this->finderMock
->expects($this->once())
->method('files')
->will($this->returnValue($this->finderMock));
$this->finderMock
->expects($this->once())
->method('in')
->will($this->returnValue($filesArray));
// act
$file = $service->getFile('/file.zip');
// assert
$this->assertInstanceOf(SplFileInfo::class, $file);
}
}
The mocked instance of the Finder class needs to implement/mock the method getIterator. The getMock method does not accept arguments so don't pass the string 'Iterator'—change the code as follows:
private function getFinderMock()
{
return $this->getMockBuilder(Finder::class)
->disableOriginalConstructor()
->getMock();
}
And add the Mocked expectation in the test method, for example:
$this->finderMock->expects($this->once())
->method('getIterator')
->willReturn(new \ArrayObject([$this->createMock(SplFileInfo::class)]));
Hope this helps.

PHPUnit: Call to undefined method

I want to run simple player inserting test.
My code is something like that:
class Player{
public $id;
public $name;
}
class PlayerDAO{
//db stuff
.....
privatefunction input($player){
$player->id = mysql_real_escape_string($player->id);
$player->name = mysql_real_escape_string($player->name);
return $player;
}
public function insert($player){
$player = $this->input($player);
mysql_query("INSERT INTO player (name) VALUES ('.$player->name.')");
}
public function countPlayers(){
$r = mysql_query("SELECT * FROM player");
return mysql_num_rows($r);
}
}
//Test class
class PlayerTest extends PHPUnit_Framework_TestCase {
public function testInsert(){
$player = new Player();
$player->name = 'Test name';
$count1 = PlayerDAO::countPlayers();
PlayerDAO::insert($player);
$count2 = PlayerDAO::countPlayers();
$this->assertEquals(($count2-$count1), 1);
}
}
If i run test, i get error message: Fatal error: Call to undefined method PlayerTest::input() in ...PlayerTest.php. If i remove method input() from insert() method, the error message is gone.

my implementation of Iterator did not work in Twig 1.12

I read that the foreach works for arrays or any objects that implement Traversable interface.
Since Traversable is a mere abstract, I used Iterator.
class EPubHub_PageCollection implements Iterator
{
protected $index0 = 0;
protected $index = 1;
protected $pages = array();
public function __construct()
{
$this->index0 = 0;
$this->index = 1;
$this->pages = array();
}
public function updateIndex()
{
$this->index = $this->index0 + 1;
}
public function rewind()
{
$this->index0 = 0;
$this->updateIndex();
}
public function current()
{
return $this->pages[$this->index0];
}
public function key()
{
return $this->index0;
}
public function index()
{
return $this->index;
}
public function next()
{
++$this->index0;
$this->updateIndex();
}
public function valid()
{
return isset($this->pages[$this->index0]);
}
public function length()
{
return count($this->pages);
}
public function prefixZero($index, $lengthOfKey = 3)
{
return str_pad($index, $lengthOfKey, '0', STR_PAD_LEFT);
}
public function getCurrentPageNumber()
{
$pageNumber = $this->prefixZero($this->index);
return $pageNumber;
}
public function getCurrentPageFilename($pagenumber = '')
{
if ($pagenumber === '')
{
$pagenumber = $this->getCurrentPageNumber();
}
$pageFileName = 'page' . $pagenumber . '.xhtml' ;
return $pageFileName;
}
public function getNth($index)
{
return $this->pages[$index - 1];
}
public function getCurrentPageItemId($pagenumber = '')
{
if ($pagenumber === '')
{
$pagenumber = $this->getCurrentPageNumber();
}
$pageItemId = 'pg' . $pagenumber;
return $pageItemId;
}
public function add(EPubHub_PageInterface $page, $index = null)
{
if ($index === null) {
// we just append to the pages from the end directly
$this->pages[] = $page;
} else {
$this->pages = array_splice( $this->pages, $index - 1, 0, $page);
}
}
/**
*
* delete either by page or by index
* #param mixed $pageOrIndex If $pageOrIndex is instance of EPubHub_PageInterface
* then delete by value. Else if integer delete by index
*/
public function delete($pageOrIndex)
{
if ($pageOrIndex instanceof EPubHub_PageInterface)
{
$page = $pageOrIndex;
if(($key = array_search($page, $this->pages)) !== false)
{
unset($this->pages[$key]);
}
}
if (is_numeric($pageOrIndex))
{
$key = $pageOrIndex;
if((array_key_exists($key - 1, $this->pages)))
{
unset($this->pages[$key - 1]);
}
}
}
public function getImages()
{
$images = new EPubHub_ImageCollection();
foreach($this->pages as $page)
{
$image = $page->getImage();
$images->add($image);
}
return $images;
}
public function getPages()
{
return $this->pages;
}
}
This one did not work.
I want to understand why. I tried running dump(), but dump on the instance broke something.
Currently, my workaround is to use the inner array of this class.
I am asking this question to satisfy my curiosity.
You need to configure the debugging:
services:
acme_hello.twig.extension.debug:
class: Twig_Extension_Debug
tags:
- { name: 'twig.extension' }
Look here: debugging. And than: for loop in twig.
For example:
{{ dump(users) }}
{% for user in users %}
{{ user.username }}
{% endfor %}

Resources