I'm having difficulty with the spy and mock in Laravel 7 test when I test for MyCustomClass.
I have tried both mock before running $this->get and spy after $this->get. Both with the same error message (*below).
When running debug in the controller the $myCustomClass is still the MyCustomClass and not the mocked object.
MyCustomClass
class MyCustomClass
{
public function execute()
{
return 'hello';
}
MyController
class MyController
{
public function show()
{
$myCustomClass = new MyCustomClass();
$data = $myCustomClass->execute();
return $data;
}
private $mySpy;
public function testAMethod()
{
$spy = $this->spy(MyCustomClass::class);
$response = $this->get('/my/path');
$spy->shouldHaveReceived('execute');
$response->assertStatus(200);
}
Error
Method execute(<Any Arguments>) from Mockery_2_App_MyCustomClass should be called
at least 1 times but called 0 times.
The problem is that you are instantiating MyCustomClass yourself with the new keyword.
In order for Laravel to be able to swap out the real class with the spy you have to use the service container.
Something like this:
class MyController
{
public function show(MyCustomClass $myCustomClass)
{
return $myCustomClass->execute();
}
}
Related
I have a Basic Class Helper.
namespace App\Helpers;
class CommonUtility {
public static function foo() {
return 'foo';
}
}
And it's used in User.php
<?php
class User extends Authenticatable
public function test() {
return CommonUtility::foo();
}
}
And now I wanted to write a test with mocking CommonUtility class. Can anyone know about this I'm new to laravel unit test.
Tried few solutions like
$mock = Mockery::mock(CommonUtility::class, function ($mock) {
$mock->shouldReceive('foo')->andReturn('foobar');
})->makePartial();
dd($mock->foo());
it gives me the mock result but i tried it from
$user = new \App\User();
dd($user->test());
It's calling the actual function instead of mock one.
You could use the Aliasing Mockery features for Mocking Public Static Methods
As example:
$mock = \Mockery::mock('alias:App\Helpers\CommonUtility');
$mock->shouldReceive('foo')
->andReturn('foobar');
$user = new User();
$this->assertEquals("foobar", $user->test());
The test pass as expected
Provider
namespace App\Providers;
class ElasticSearchProvider extends ServiceProvider
{
public function register()
{
$hosts = [
'elasticsearch'
];
$instance = Elasticsearch\ClientBuilder::create()
->setHosts($hosts)
->build();
$this->app->instance('App\ESClient', $instance);
}
}
Actual Class
namespace App\Mappings;
class Categories implements Mappable
{
public $es;
public function __construct(App\ESClient $es)
{
$this->es = $es;
}
public function setMapping()
{
}
public function getMapping()
{
}
}
Test Case
use App\Mappings\Categories;
class CategoriesTest extends TestCase
{
private $instance;
public function testShouldReturnElasticSearchInstance()
{
$categories = new Categories();
dd($categories->es);
}
}
1) CategoriesTest::testShouldReturnElasticSearchInstance
ErrorException: Argument 1 passed to
App\Mappings\Categories::__construct() must be an instance of
App\Mappings\App\ESClient, none given,
So in here DI is not working, or i have register something wrong how can we test it ?
Thanks
You can use Mockery for test case. So in this example your test case would looks like:
use App\ESClient;
use App\Mappings\Categories;
use Mockery as m;
class CategoriesTest extends TestCase
{
private $instance;
public function testShouldReturnElasticSearchInstance()
{
$esClient = m::mock(ESClient::class);
$categories = m::mock(new Categories($esClient));
dd($categories->es);
}
}
This will provides you a mocked class of App\ESClient that you will inject into partialy mocked class of App\Mappings\Categories. When you learn to use mockery in your unit test you will find that this is the best opion for test purpose - because mock object (and Laravel Facades) can be override while testing and can catch every method call of the object in test (with shouldReceive method of the mocked class).
Target.php
<?php
class Target
{
public function validate()
{
$this->getData();
return true;
}
public function getData()
{
return array();
}
}
TargetTest.php
<?php
class TargetTest extends PHPUnit_Framework_TestCase
{
public function testValidate()
{
$mock = m::mock('Target');
$mock->shouldReceive('getData')
->once();
$expected = $this->exp->validate();
$this->assertTrue($expected);
}
}
result
Mockery\Exception\InvalidCountException: Method getData() from Mockery_1_ExpWarning should be called
exactly 1 times but called 0 times.
I use Mockery as mock tool, the example always about how to mock with DI, I would like to know can I mock internal method?
You can use the Partial Mocks features of the testing framework for mock only the getData method and describe an expectation.
As (working) Example:
use Mockery as m;
class TargetTest extends \PHPUnit_Framework_TestCase
{
public function testValidate()
{
$mock = m::mock('Target[getData]');
$mock->shouldReceive('getData')
->once();
$expected = $mock->validate();
$this->assertTrue($expected);
}
}
Hope this help
I'm trying to test a method of a class in my laravel project with mockery.
However, when I attempt to test the phpunit says a my interface class (which is being used in the method I need to test) is not instantiable.
What's wrong?
My test class
class HelperRSTest extends TestCase {
public function tearDown()
{
Mockery::close();
}
public function test_mockery()
{
// $mock = Mockery::mock('HelperRS');
// $mock->shouldReceive('getRecibosLocacao')->once()->andReturn('mocked');
$result = HelperRS::getRecibosLocacao(1228);
var_dump($result);
}
}
My target class to test
class HelperRS extends \BaseController {
public static function getRecibosLocacao($id_locacao){
$pagamentos = App::make('PagamentoRepositoryInterface');
$locacao = Locacao::find($id_locacao);
$pagamento = $pagamentos->getByVendaByTipo($locacao->cod_locacao, 'l');
dd($pagamento);
}
}
The error:
1) HelperRSTest::test_mockery
Illuminate\Container\BindingResolutionException: Target [PagamentoRepositoryInterface] is not instantiable.
The method you are calling on HelperRS is a static method, whereas you are creating a mocked instance of that class and not actually doing anything with it. You can achieve this with mockery, though it is not recommended and will require you to run the test in process isolation.
$mock = Mockery::mock('alias:HelperRS');
$mock->shouldReceive('getRecibosLocacao')->once()->andReturn('mocked');
This came up trying to write a test for a method of a class that calls a mock method with a closure. How would you verify the closure being called?
I know that you would be able to assert that the parameter is an instance of Closure. But how would you check anything about the closure?
For example how would you verify the function that is passed:
class SUT {
public function foo($bar) {
$someFunction = function() { echo "I am an anonymous function"; };
$bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase {
public function testFoo() {
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->with( /** WHAT WOULD I ASSERT HERE? **/);
$sut = new SUT();
$sut->foo($mockBar);
}
}
You can't compare two closures in PHP. Is there a way in PHPUnit to execute the parameter passed in or in some way verify it?
If you want to mock an anonymous function (callback) you can mock a class with __invoke method. For example:
$shouldBeCalled = $this->getMock(\stdClass::class, ['__invoke']);
$shouldBeCalled->expects($this->once())
->method('__invoke');
$someServiceYouAreTesting->testedMethod($shouldBeCalled);
If you are using latest PHPUnit, you would have to use mock builder to do the trick:
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->setMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke');
$someServiceYouAreTesting->testedMethod($shouldBeCalled);
You can also set expectations for method arguments or set a returning value, just the same way you would do it for any other method:
$shouldBeCalled->expects($this->once())
->method('__invoke')
->with($this->equalTo(5))
->willReturn(15);
Your problem is that you aren't injecting your dependency (the closure), which always makes unit testing harder, and can make isolation impossible.
Inject the closure into SUT::foo() instead of creating it inside there and you'll find testing much easier.
Here is how I would design the method (bearing in mind that I know nothing about your real code, so this may or may not be practical for you):
class SUT
{
public function foo($bar, $someFunction)
{
$bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase
{
public function testFoo()
{
$someFunction = function() {};
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->with($someFunction);
$sut = new SUT();
$sut->foo($mockBar, $someFunction);
}
}
If the closure has some side effects inside SUT that could be verified by the test after the mock invocation, use returnCallback to provide another closure to be called with the passed arguments and have its return value returned to SUT. This will allow you to call SUT's closure to cause the side effects.
class SUT {
public function foo($bar) {
$someFunction = function() { return 5 * 3; };
return $bar->baz($someFunction);
}
}
class SUTTest extends PHPUnit_Framework_TestCase {
public function testFoo() {
$mockBar = $this->getMockBuilder('Bar')
->setMethods(array('baz'))
->getMock();
$mockBar->expects($this->once())
->method('baz')
->will($this->returnCallback(function ($someFunction) {
return $someFunction();
}));
$sut = new SUT();
self::assertEquals(15, $sut->foo($mockBar));
}
}