I'm learning phpunit since week. I don't have idea how to mock only one method from tested class. (it's example only so I didn't write namespaces). Maybe you can help me
class SomeService
{
public function firstMethod()
{
return 'smth';
}
public function secondMethd()
{
return $this->firstMethod() . ' plus some text example';
}
}
and test:
class SomeServiceUnitTest extends TestCase
{
private $someService;
public function setUp()
{
parent::setUp();
$this->someService = new SomeService();
}
public function tearDown()
{
$this->someService = null;
parent::tearDown();
}
public function test_secondMethod()
{
$mock = Mockery::mock('App\Services\SomeService');
$mock->shouldReceive('firstMethod')->andReturn('rerg');
exit($this->walletService->secondMethd());
}
}
You can use a partial mocks, as example on your test class, you can do:
public function test_secondMethod()
{
$mock = Mockery::mock('App\Services\SomeService')->makePartial();
$mock->shouldReceive('firstMethod')->andReturn('rerg');
$this->assertEquals('rerg plus some text example', $mock->secondMethd());
}
Hope this help
Related
I have a problem, I cannot call a function before all functions.
I have a parent class - that is a repository.
class Repository {
public function find(/*..*/){/*..*/}
public function findAll(/*..*/){/*..*/}
public function findBy(/*..*/){/*..*/}
public function findA(/*..*/){/*..*/}
public function findB(/*..*/){/*..*/}
/* then 100+ more public function */
}
And I want to create an "Adapter class" that will be calling a function that run before all only public functions BUT I don't want "overwriting" all functions of the parent.
I tried this solution:
class OwnRepo extends Repository{
public __call($methods, $args){/**/}
}
BUT the __call method not working with public methods
How can I solve this problem?
Thank you!
** UPDATE **
Sorry I was not clear!
My controller implements a model, I don't want to change/rewrite the functions of the controllers.
class IndexController {
public function index(){
$model = new Model(); // will return a OwnRepo object
$a = model->findAll();
}
}
In my opinion you can't do this with inheritance. You have to check if the desired method exists in the Repository class and then call it.
Try this one:
class Repository
{
public function find($a,$b,$c)
{
return "find $a $b $c";
}
}
class OwnRepo
{
function __call($name, $arguments)
{
if (method_exists('Repository', $name)) {
//some action before
$repo = new Repository();
$result = call_user_func_array(array($repo, $name), $arguments);
//some action afterwards
return $result;
} else {
die("Method " . $name . " does not exist");
}
}
}
$o = new OwnRepo();
echo $o->find(1,2,3);
I'm happily writing unit tests, but they clash when I run them all together. I'm testing this class:
class MyClass {
public function sayHello() {
return 'Hello world';
}
}
using this test. All tests have a structure like this:
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public static function setUpBeforeClass() {
require_once('path/to/MyClass.php');
}
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
MyClassTest runs fine on its own. But PHPUnit will crash because I redeclare the class, if another test mocks MyClass and happens to run first:
class Inject {
private $dependency;
public function __construct(MyClass $myClass) {
$this->dependency = $myClass;
}
public function printGreetings() {
return $this->dependency->sayHello();
}
}
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals('Hello world', $subject->printGreetings());
}
}
I do use a bootstrap.php to fake some global functions not yet refactored.
I have no auto loaders and don't want to process-isolate EVERY test, because it takes forever. I tried inserting combinations #runTestsInSeparateProcesses and #preserveGlobalState enabled/disabled in the docblocks of both Test 1 & 2, I still get the same error.
To understand this behaviour, you need to have a look at how PHPUnit works. getMockBuilder()->getMock(), dynamically creates the following code for the mock class:
class Mock_MyClass_2568ab4c extends MyClass implements PHPUnit_Framework_MockObject_MockObject
{
private static $__phpunit_staticInvocationMocker;
private $__phpunit_invocationMocker;
public function __clone()
{
$this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker();
}
public function sayHello()
{
$arguments = array();
$count = func_num_args();
if ($count > 0) {
$_arguments = func_get_ ...
# more lines follow ...
If MyClass hasn't already been loaded at this time, it adds the following dummy declaration:
class MyClass
{
}
This code will then getting parsed using eval() (!).
Since PHPUnit will execute InjectTest before MyClassTest, this means that the mock builder will define the dummy class and MyClass is already defined when MyClassTest::setUpBeforeClass will get called. That's why the error. I hope I could explain. Otherwise, dig into PHPUnit's code.
Solution:
Drop the setUpBeforeClass() method and put the require_once statement on top of the tests. setUpBeforeClass() is not meant for including classes. Refer to the docs.
Btw, having require_once on top will work because PHPUnit will include every test file before starting the first test.
tests/MyClassTest.php
require_once __DIR__ . '/../src/MyClass.php';
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
tests/InjectTest.php
require_once __DIR__ . '/../src/Inject.php';
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals(TRUE, $subject->printGreetings());
}
}
I'm new to testing and I am trying to create a unit test that covers the first if statement in the NewsCreator create method.
This question has two parts.
First: How should I be instantiating NewsCreator to handle the mocked validator and repository?
Second: What would the correct way to test this path be?
Here is my controller method that calls the class that needs testing:
public function store()
{
$creator = new NewsCreator($this);
return $creator->create(Input::all());
}
Here is the class that I wish to test, NewsCreator:
<?php namespace WHS\Portal\News;
class NewsCreator {
protected $listener;
protected $repository;
protected $validator;
protected $errors;
public function __construct($listener, NewsRepositoryInterface $repository, NewsValidator $validator)
{
$this->listener = $listener;
$this->repository = $repository;
$this->validator = $validator;
$this->errors = [];
}
public function create($data)
{
if($this->validator->fails($data))
{
return $this->listener->newsCreationFails($this->validator->messages());
}
if($this->repository->create($data))
{
return $this->listener->newsCreationSucceeds();
}
return $this->listener->newsCreationFails($this->errors);
}
}
This is the test I attempted to write, but it fails
with exception:
2) WHS\Portal\Tests\News\NewsCreatorTest::test_failed_validation Mockery\Exception\InvalidCountException: Method fails("foo") from Mockery_1_WHS_Portal_News_NewsValidator should be called exactly 1 times but called 0 times.
<?php namespace WHS\Portal\Tests\News;
use TestCase;
use Mockery as m;
class NewsCreatorTest extends TestCase {
public function tearDown()
{
m::close();
}
public function test_failed_validation()
{
$newsRepo = m::mock('\WHS\Portal\News\DbNewsRepository["create"]');
$newsValidator = m::mock('\WHS\Portal\News\NewsValidator["fails"]');
$listener = new NewsListenerStub();
$listener = m::mock($listener)->makePartial();
$newsValidator->shouldReceive('fails')->with('foo')->once()->andReturn(true);
$listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn();
$newsCreator = new \WHS\Portal\News\NewsCreator($listener,$newsRepo,$newsValidator);
$newsCreator->create([]);
}
}
Updated test:
use TestCase;
use Mockery as m;
class NewsCreatorTest extends TestCase {
public function tearDown()
{
m::close();
}
public function test_failed_validation()
{
$newsRepo = m::mock('\WHS\Portal\News\DbNewsRepository["create"]');
$newsValidator = m::mock('\WHS\Portal\News\NewsValidator["fails"]');
$listener = m::mock('\WHS\Portal\Tests\News\NewsListenerStub["newsCreationFails"]');
$newsValidator->shouldReceive('fails')->with([])->once()->andReturn(true);
$newsValidator->shouldReceive('messages')->once();
$listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn('foo-bar');
$newsCreator = new \WHS\Portal\News\NewsCreator($listener,$newsRepo,$newsValidator);
$result = $newsCreator->create([]);
$this->assertEquals('foo-bar', $result);
}
}
The stub class:
class NewsListenerStub
{
public function newsCreationFails($data)
{
return $data;
}
}
Please help.
Thanks.
The method fails() is called with the $data argument in your class.
In your unit test your are passing in an empty array as data create([]).
Your argument expectation on the validatorMock is expecting fails() to be called with the parameter foo. You have to alter that to match the empty array.
$newsValidator->shouldReceive('fails')->with([])->once()->andReturn(true);
Also you have to specify the validator->messages() method on the validatorMock because that is also being called in your class.
$newsValidator->shouldReceive('messages')->once();
For this test to really make sence you have to assert that the result of NewsCreationFails matches the return value of create().
$listener->shouldReceive('newsCreationFails')->once()->with('foo')->andReturn('foo-bar');
...
$result = $newsCreator->create([]);
$this->assertEquals('foo-bar', $result);
I am writing some tests for a Magento module, using Ivan Chepurnyi's extension, and I'm having trouble using the mock objects.
Here is the class:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
public function __construct()
{
$this->_salesCollection = Mage::getModel('module/classA')->getCollection()
->addFieldToFilter('id', $this->_getId());
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_salesCollection->count();
}
}
The method I'm trying to test is getSalesTotalNumber().
And here is the test:
<?php
class Namespace_Module_Test_Block_Class extends EcomDev_PHPUnit_Test_Case
{
private $_mock;
public function setUp()
{
$this->_mock = $this->getMock('Namespace_Module_Block_Class',
array('_getId')
);
$this->_mock->expects($this->any())
->method('_getId')
->will($this->returnValue(1024));
parent::setUp();
}
/**
* #test
* #loadFixture
* #loadExpectation
*/
public function testSalesTotalNumber()
{
$actual = $this->_mock->getSalesTotalValue();
$expected = $this->_getExpectations()->getSalesTotalNumber();
$this->assertEquals($expected, $actual);
}
}
As you can see, what I want to do is overwrite the _getId() method so that it returns an id which match the id in the fixture and so load the collection. But it doesn't work :-(.
In my test, if I echo $this->_mock->_getId() it returns the correct Id (1024). But in the __construct() of my class $this->_getId() returns null, which is the expected value during testing (I mean, during testing there is no session, so it can't get the object's Id as I store it in a session variable). So the _getId() method isn't mocked by my test case.
Any help will be highly appreciated.
So my problem was not in the mock/test but in the class.
I have moved the content of __construct() into a protected method which returns the collection object. That's how my class looks like now:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
protected function _getAffiliateSales()
{
if (is_null($this->_salesCollection)) {
$affiliateId = $this->_getId();
$this->_salesCollection = Mage::getModel('module/classA')
->addFieldToFilter('id', $affiliateId);
}
return $this->_salesCollection;
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_getAffiliateSales()->count();
}
}
I have a simple question. I use a singleton which implements an abstract class. Is it possible to put the getInstance() Method and the variable $_instance in the abstract class instead of the concrete one I want to create?
Here's my code:
<?php
class Command_Log extends Command_Abstract {
private static $_instance=null;
public static function getInstance() {
if (self::$_instance==null)
self::$_instance=new self();
return self::$_instance;
}
protected function realExecute() {
}
protected function realSimulate($fileHandle) {
}
}
and
<?php
abstract class Command_Abstract implements Command_Interface {
protected $_data=array();
//private static $_instance=null;
protected $_isExecuted=false;
protected $_execute=false;
public function enableExecute() {
$this->_execute=true;
return $this;
}
protected function __construct() {
}
protected function __clone() {}
public function addData($data) {
array_push($this->_data,$data);
return $this;
}
abstract protected function realExecute();
abstract protected function realSimulate($fileHandle);
public function execute() {
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$this->realExecute();
}
}
public function simulate() {
$exitSystem = false;
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$exitSystem = $this->realSimulate($fh);
}
}
return $exitSystem;
}
}
I have many implementations of the the commands, so I don't want redundant code everywhere in my implementations. Is it possible to put these two things in the abstract class, if yes please tell me how.
If not please explain it to me why it isnt possbile. Or if I need to change something to do it, anyhow.
regards
YES WE CAN!
I have a class called Singleton that is abstract... and many classes that extends that Singleton class... this is the code:
abstract class Singleton {
private static $instances = array();
final private function __construct($_params) {
$class = get_called_class();
if (array_key_exists($class, self::$instances))
throw new Exception('An instance of '. $class .' already exists !');
//static::initialize(); //In PHP 5.3
$this->initialize($_params);
}
final private function __clone() { }
abstract protected function initialize();
public static function getInstance($_params=array()) {
$class = get_called_class();
if (array_key_exists($class, self::$instances) === false){
self::$instances[$class] = new $class($_params);
}
return self::$instances[$class];
}
}
and (for example) the class DBConnection that extends from Singleton
class DBConnection extends Singleton{
private $connexion_pdo=null;
protected function initialize(){
//connect to the DB
$this->connexion_pdo = blablalba;
}
}
although there are some problems in php 5.2.. specially with the function get_called_class() and the static::initialize()
You could also check the php site for patterns... there are lots of contributions for the singleton
Good Luck