Laravel + Mockery InvalidCountException
up vote
0
down vote
favorite
I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson()
by manually creating instances of the Request
- and OEmbedController
-classes. The create()
-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
MockeryExceptionInvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* @test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
laravel mockery
add a comment |
up vote
0
down vote
favorite
I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson()
by manually creating instances of the Request
- and OEmbedController
-classes. The create()
-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
MockeryExceptionInvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* @test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
laravel mockery
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
A tip: you can rename the__invoke
method tohandle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the$request
itself. Do add more info about route you are accessing.
– Kyslik
Nov 18 at 13:02
So you are basically saying I should dependency inject the class? Like$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.
– Fredrik
Nov 18 at 13:07
1
You do not need an interface, just use->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on aftercreate
is called. But in the controller you can now use$info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the testsetUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…
– Kyslik
Nov 18 at 13:23
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson()
by manually creating instances of the Request
- and OEmbedController
-classes. The create()
-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
MockeryExceptionInvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* @test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
laravel mockery
I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson()
by manually creating instances of the Request
- and OEmbedController
-classes. The create()
-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
MockeryExceptionInvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* @test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
laravel mockery
laravel mockery
edited Nov 18 at 14:23
asked Nov 18 at 12:41
Fredrik
565224
565224
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
A tip: you can rename the__invoke
method tohandle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the$request
itself. Do add more info about route you are accessing.
– Kyslik
Nov 18 at 13:02
So you are basically saying I should dependency inject the class? Like$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.
– Fredrik
Nov 18 at 13:07
1
You do not need an interface, just use->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on aftercreate
is called. But in the controller you can now use$info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the testsetUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…
– Kyslik
Nov 18 at 13:23
add a comment |
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
A tip: you can rename the__invoke
method tohandle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the$request
itself. Do add more info about route you are accessing.
– Kyslik
Nov 18 at 13:02
So you are basically saying I should dependency inject the class? Like$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.
– Fredrik
Nov 18 at 13:07
1
You do not need an interface, just use->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on aftercreate
is called. But in the controller you can now use$info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the testsetUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…
– Kyslik
Nov 18 at 13:23
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
A tip: you can rename the
__invoke
method to handle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the $request
itself. Do add more info about route you are accessing.– Kyslik
Nov 18 at 13:02
A tip: you can rename the
__invoke
method to handle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the $request
itself. Do add more info about route you are accessing.– Kyslik
Nov 18 at 13:02
So you are basically saying I should dependency inject the class? Like
$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.– Fredrik
Nov 18 at 13:07
So you are basically saying I should dependency inject the class? Like
$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.– Fredrik
Nov 18 at 13:07
1
1
You do not need an interface, just use
->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on after create
is called. But in the controller you can now use $info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the test setUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…– Kyslik
Nov 18 at 13:23
You do not need an interface, just use
->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on after create
is called. But in the controller you can now use $info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the test setUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…– Kyslik
Nov 18 at 13:23
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
It seems you are mocking the Embed
class the wrong way. If you use the Laravel facade method shouldReceive()
instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url)
, the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
TheEmbed
class isn't a Facade though. Its namespace is EmbedEmbed.
– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
add a comment |
up vote
0
down vote
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
It seems you are mocking the Embed
class the wrong way. If you use the Laravel facade method shouldReceive()
instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url)
, the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
TheEmbed
class isn't a Facade though. Its namespace is EmbedEmbed.
– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
add a comment |
up vote
1
down vote
It seems you are mocking the Embed
class the wrong way. If you use the Laravel facade method shouldReceive()
instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url)
, the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
TheEmbed
class isn't a Facade though. Its namespace is EmbedEmbed.
– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
add a comment |
up vote
1
down vote
up vote
1
down vote
It seems you are mocking the Embed
class the wrong way. If you use the Laravel facade method shouldReceive()
instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url)
, the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
It seems you are mocking the Embed
class the wrong way. If you use the Laravel facade method shouldReceive()
instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url)
, the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
answered Nov 18 at 13:24
Namoshek
2,6552718
2,6552718
TheEmbed
class isn't a Facade though. Its namespace is EmbedEmbed.
– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
add a comment |
TheEmbed
class isn't a Facade though. Its namespace is EmbedEmbed.
– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
The
Embed
class isn't a Facade though. Its namespace is EmbedEmbed.– Fredrik
Nov 18 at 13:30
The
Embed
class isn't a Facade though. Its namespace is EmbedEmbed.– Fredrik
Nov 18 at 13:30
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
So you are trying to mock a static method? I'm afraid that's not going to work.
– Namoshek
Nov 18 at 13:31
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
Didn't know you can't mock static methods without using Facades.
– Fredrik
Nov 18 at 13:33
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
That's actually a limitation of the mocking libraries, not Laravel. Facades only provide static access to methods; it doesn't mean the methods itself are static (in fact, they are never as far as I know). So the easiest way to achieve testability could be to build your own proxy class around it... (which doesn't use static methods).
– Namoshek
Nov 18 at 13:37
add a comment |
up vote
0
down vote
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);
add a comment |
up vote
0
down vote
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);
add a comment |
up vote
0
down vote
up vote
0
down vote
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);
answered Nov 18 at 14:25
Fredrik
565224
565224
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53360987%2flaravel-mockery-invalidcountexception%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
You do not need to test the embed package; its already tested, right?
– Kyslik
Nov 18 at 12:51
Yes, so my thoughts were to mock the response form that class.
– Fredrik
Nov 18 at 12:54
A tip: you can rename the
__invoke
method tohandle
it'll look nicer. You are creating an instance of Embed within the (invoke) method; thats untestable without the mock. I personally avoid mocking anything and resort to stubs / fake classes etc. I do recommend letting the Laravel build / create / instantiate the Embed object before it hits the controller. Which means creating a service provider which will take care of the instantiating the Embed object for you and perhaps placing it in the$request
itself. Do add more info about route you are accessing.– Kyslik
Nov 18 at 13:02
So you are basically saying I should dependency inject the class? Like
$this->app->bind()
. Sure that is possible, but shouldn't it be possible without that? Because then I need an Interface, etc, which feels cumbersome for this little test.– Fredrik
Nov 18 at 13:07
1
You do not need an interface, just use
->bind(EmbedEmbed::class, function($app){ return EmbedEmbed::create($app['request']->url); });
Now I do not know whats going on aftercreate
is called. But in the controller you can now use$info = resolve(EmbedEmbed::class)
; obviously you need to do checking if request has url parameter etc (do this within the bind closure). Now in the testsetUp
you need to rebind it, for more information read stackoverflow.com/questions/50262576/…– Kyslik
Nov 18 at 13:23