PHPunit Mocking not returning specified value - 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");

Related

Partial mocking a Facade ignores the variables that are set in the constructor

I have a class names AbcService
<?php
namespace App\Http;
class AbcService
{
private string $attr;
public function __construct()
{
$this->attr = 'some text';
}
public function aMethod(): bool
{
if (empty($this->attr)) {
return true;
}
return false;
}
public function bMethod()
{
return 'what';
}
}
I've created a facade on top of that:
<?php
namespace App\Http;
use Illuminate\Support\Facades\Facade;
class Abc extends Facade
{
protected static function getFacadeAccessor()
{
return AbcService::class;
}
}
Now calling this code from the tink session (php artisan tink) outputs true, meaning that the attr value did not change in the constructor.
use App\Http\Abc;
Abc::partialMock();
Abc::shouldReceive('bMethod')->once()->andReturn('partialMock');
print_r(Abc::aMethod());
I've placed a dd in the constructor after changing the value of attr and we know after calling the Abc::partialMock() the constructor executed. Is this the normal behavior of the partialMock in the Facade? How can I only mock the bMethod method and use other part of the class without changes?

Laravel Why I can access the method was not declared from Interface?

I have a problem like below:
I have an Interface name IBannerService
<?php
namespace App\Interfaces;
interface IBannerService
{
public function add($data);
public function list();
public function get($data);
public function delete($data);
}
and an instance name BannerService
class BannerService implements IBannerService
{
public function add($data)
{
return true;
}
public function list()
{
return true;
}
public function get($data)
{
return true;
}
public function delete($data)
{
return true;
}
public function test()
{
print_r("aaaa");
die();
}
}
finally I have a Controller name HomeController
class HomeController extends Controller
{
public function __construct(
IBannerService $bannerService
)
{
$this->bannerService = $bannerService;
}
public function index()
{
$listBanner = $this->bannerService->list();
$this->bannerService->test();
}
}
My configuration:
class DIServiceProvider extends ServiceProvider
{
$this->app->bind(
'App\Interfaces\IBannerService',
'App\Services\BannerService'
);
}
In app.php:
'providers'=>[
App\Providers\DIServiceProvider::class,
]
The code run well with $listBanner = true (just for testing).
The problem is:
Test Method was not declared in interface IBannerService but still go through and print out "aaa" the die.
Did I do something wrong?
Please suggest me, thank you!
That's perfectly normal functionality.
In the Laravel container you defined that when you ask for a IBannerService object, you want to get a BannerService class. And that is what you got. BannerService is an implementation of IBannerService, so no problem for the typehint.
A class is not limited to the functions defined by its interface so you can add as many other functions as you like. I wouldn't recommend it though, things like smart IDE's and phpstan would give you errors or warnings because to them, the variable is an implementation of IBannerService and this does not have a test() function.
If you really want to use more functions I would even recommend to use BannerService as the typehint. This way, static code analysis will still work.

Mock method from the same class that tested method is using

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

Phpunit, how to mock an abstract class?

Im talking about this function:
function testMeSomehow ($id, Flag $flags)
{
$flags::NAME;
}
its parameter object:
abstract class Flag
{
abstract function method1();
abstract function method2();
.
.
.
abstract function method999();
}
how to mock this Flag class? It has tons of abstract methods, should I create all of them with empty body? And what if this class changes? I also have to add a NAME constant to it
You can mock it with test doubles like you would do for any other class, see https://phpunit.de/manual/current/en/test-doubles.html
This could be an example test:
class TargetClass
{
public function testMeSomehow($id, Flag $flag)
{
return $flag->method1();
}
}
class TargetClassTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$mock = $this->getMock('Flag');
$mock->expects($this->once())
->method('method1')
->willReturn('methodResult');
$targetClass = new TargetClass();
$this->assertEquals('methodResult', $classToTest->testMeSomehow(1, $mock));
}
}
You can specify the methods that you want to replace by the mock as the 2nd parameter of $this->getMock(). Because we don't specify anything at all, it will replace all methods and thus won't bother about the abstract methods.
Edit: added an example to access constants:
class ClassToTest
{
public function testMeSomehow($id, Flag $flag)
{
return $flag::NAME;
}
}
class FlagTest extends PHPUnit_Framework_TestCase
{
public function testStuff()
{
$mock = $this->getMock('Flag');
$classToTest = new ClassToTest();
$this->assertEquals('Flag name', $classToTest->testMeSomehow(1, $mock));
}
}
If you want a specific value for that constant in your tests, I suggest to make a child class with this constant and use that for mocking.
Edit: added an example with a workaround to specify dynamic constant values.
class Flag
{
const NAME = 'Flag name';
public function getName()
{
return static::NAME;
}
}
class TargetClass
{
public function testMeSomehow($id, Flag $flag)
{
return $flag->getName();
}
}
class TargetClassTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$mock = $this->getMock('Flag');
$mock->expects($this->any())
->method('getName')
->willReturn('New flag name');
$targetClass = new TargetClass();
$this->assertEquals('New flag name', $classToTest->testMeSomehow(1, $mock));
}
}
However, this compels you to use the getName() method everywhere, so I personally prefer the previous suggestion: mocking a child class that has the changed value.

PHPUnit Stubbing Class methods declared as "final"

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

Categories