I have following code:
class Foo() {
public function someMethod() {
...
if ($this->otherMethod($lorem, $ipsum)) {
...
}
...
}
}
and I'm trying to test the someMethod(), I don't want to test otherMethod() since it's quite complex and I have dedicated tests - here I would only like to mock it and return specific values.
So I tried to:
$fooMock = Mockery::mock(Foo::class)
->makePartial();
$fooMock->shouldReceive('otherMethod')
->withAnyArgs()
->andReturn($otherMethodReturnValue);
and in test I'm calling
$fooMock->someMethod()
But it's using the original (not mocked) method otherMethod() and prints errors.
Argument 1 passed to Mockery_3_Foo::otherMethod() must be an instance of SomeClass, boolean given
Could you help me please?
Use this as a template to mock a method:
<?php
class FooTest extends \Codeception\TestCase\Test{
/**
* #test
* it should give Joy
*/
public function itShouldGiveJoy(){
//Mock otherMethod:
$fooMock = Mockery::mock(Foo::class)
->makePartial();
$mockedValue = TRUE;
$fooMock->shouldReceive('otherMethod')
->withAnyArgs()
->andReturn($mockedValue);
$returnedValue = $fooMock->someMethod();
$this->assertEquals('JOY!', $returnedValue);
$this->assertNotEquals('BOO!', $returnedValue);
}
}
class Foo{
public function someMethod() {
if($this->otherMethod()) {
return "JOY!";
}
return "BOO!";
}
public function otherMethod(){
//In the test, this method is going to get mocked to return TRUE.
//that is because this method ISN'T BUILT YET.
return false;
}
}
Related
I am using PHPStorm.
As an example, I have a simple query class with all sorts of methods to take care of sorting, filtering, ect. It eventually returns an instance of the class passed in the constructor.
class AnimalQuery {
private $classString;
public function __construct(string $classString){
$this->classString = $classString;
}
public function where(string $key, string $val): AnimalQuery {
// Database things would happen here
return $this;
}
public function orderBy(string $val): AnimalQuery {
// Database things would happen here
return $this;
}
public function first() {
// Database things would happen here
return new $this->classString();
}
}
And a class that acts as a model, for example:
class Dog {
public static function query(): AnimalQuery {
return new AnimalQuery(static::class);
}
public function bark() {
echo 'Woof!';
}
}
It can be called like so:
$doggo = Dog::query()->where('name', 'Fido')->orderBy('species')->first();
$doggo->bark();
This does return an instance of Dog. However, PHPStorm of course does not understand that. Is there a way to clue PHPStorm in that the first method returns an instance of the class passed in the constructor of AnimalQuery?
In Typescript I might've used generics for this, but those do not exist in PHP.
I have a my controller class like below
Class UserController {
public function getName(){
return "test_name"
}
public function hello(){
return getName()."!"
}
}
An accompanying unit test as seen below
use PHPUnit\Framework\TestCase;
use App\Http\Controllers\API\User\UserController;
class UserTest extends TestCase{
public function testExample(){
$mockGetName = $this->getMockBuilder(UserController::class)->onlyMethods(['getName'])->getMock();
$mockGetName->method('getName')->willReturn("dummy");
$userController = new $mockGetName();
dump($userController->getName("test name"));
}
}
When mocked getName() returns null instead of "dummy". Anyone have any idea why the specified return value is not being returned?
Figured it out. I used createPartialMock instead of the mockBuilder and it returns what is needed.
$mockGetName = $this->createPartialMock(UserController::class, ['getName']);
$mockGetName->method('getName')->willReturn("test name");
I would like to have a PHPUnit Mock which executes a method like normal, but then modifies the return value in some way before the function returns.
What I have
I have a set of derived classes, similar to below:
abstract class Base
{
abstract protected function getUrl();
public function callUrl() {
$url = $this->getUrl();
// some code to call the URL here
}
}
class Foo extends Base
{
protected function getUrl() {
return "http://www.example.com/Foo";
}
}
class Bar extends Base
{
protected function getUrl() {
return "http://www.example.com/Bar";
}
}
Please note the classes I have are much more complex, and some of the items I have to test have side-effects (such as writing to a database, etc).
The naive, duplicate code approach
If I only had a single derived class (eg; Foo), then I could do the following:
class FooMock extends Foo
{
protected function getUrl() {
return parent::getUrl() . "?sandbox";
}
}
class theTest extends PHPUnit_Framework_TestCase
{
public function testIt() {
$mock = new FooMock();
// assert something
}
}
Unfortunately, this means I would need a specific "Mock" class for each derived class I want to test, all of which perform exactly the same function.
The preferred approach
Instead, I would like to be able to do something like the following:
function callback ($returnValue) {
return $returnValue . "?sandbox";
}
class theTest extends PHPUnit_Framework_TestCase
{
private $mock;
public function testFoo() {
$this->mock = $this->getMockBuilder('Foo')->getMock();
$this->setupMock();
// assert something
}
public function testBar() {
$this->mock = $this->getMockBuilder('Bar')->getMock();
$this->setupMock();
// assert something
}
public function setupMock() {
$this->mock->expects($this->any())
->method('getUrl')
->will($this->postProcessReturnValue('callback'));
}
}
Is this at all possible with PHPUnit?
Update: It was suggested I have an instance of the original class, and an instance of the mock class. Use the original class to get the original return value and modify that. This modified value is then used as the return for the Mock. This is not a feasible way to go about things as the classes are more complex (they have side effects such as writing to the DB).
An example where this would not work;
class Foo extends Base
{
$id = 0;
public function saveToDB() {
$this->id = saveToDBAndReturnId();
}
protected function getUrl() {
if ($this->id > 0) {
return "http://www.example.com/".$this->id;
}
throw new Exception("No ID");
}
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();
Obviously the returned URL would be different between multiple calls. I could always mock saveToDB, but that's starting to feel dirty when all I want to do is post-process the result of getUrl.
PHPUnit allows you to define a stub method that will use a callback to determine what to return.
$this->mock->expects($this->any())
->method('getUrl')
->will($this->returnCallback('callback'));
You can define your callback to call the original class method and modify the return value.
Of course, using mock objects in this way more or less defeats the purpose of having them be "mock" objects, since the mock objects will now rely on the underlying implementation.
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'm writing a unit test for a class method that calls another class's method using a mock, only the method that needs to be called is declared as final, so PHPUnit is unable to mock it. Is there a different approach I can take?
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
}
}
Edit: If using the solution provided by Mike B and you have a setter/getter for the object you're mocking that does type checking (to ensure the correct object was passed into the setter), you'll need to mock the getter on the class you're testing and have it return the other mock.
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
mock
class Class_To_MockMock
{
public function needsToBeCalled($options)
{
...
}
}
class to be tested
class Class_To_Be_Tested
{
public function setClassToMock(Class_To_Mock $classToMock)
{
...
}
public function getClassToMock()
{
...
}
public function doSomething()
{
$this->getClassToMock()
->needsToBeCalled(array('option'));
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));
$mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));
$classToTest->expects($this->any())
->method('getClassToMock')
->will($this->returnValue($mock));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
$classToTest->doSomething();
}
}
I don't think PHPUnit supports stubbing/mocking of final methods. You may have to create your own stub for this situation and do some extension trickery:
class myTestClassMock {
public function needsToBeCalled() {
$foo = new Class_To_Mock();
$result = $foo->needsToBeCalled();
return array('option');
}
}
Found this in the PHPUnit Manual under Chapter 11. Test Doubles
Limitations
Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.
I just stumbled upon this issue today. Another alternative is to mock the interface that the class implements, given that it implements an interface and you use the interface as type hinting.
For example, given the problem in question, you can create an interface and use it as follows:
interface Interface_To_Mock
{
function needsToBeCalled($options);
}
class Class_To_Mock implements Interface_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
class Class_To_Be_Tested
{
public function setClassToMock(Interface_To_Mock $classToMock)
{
...
}
...
}
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Interface_To_Mock', array('needsToBeCalled'));
...
}
}