framework: app helper doesn't return mocked instance when parameters are present

  • Laravel Version: 5.4.24
  • PHP Version: 7.0.15

When binding a mock instance to a class, resolving that class acts differently depending on whether parameters are passed in:

$mock = \Mockery::mock(SomeClass::class);
app()->instance(SomeClass::class, $mock);

get_class(app(SomeClass::class)) yields Mockery_0_SomeClass which is the mockery instance

when parameters are passed in: get_class(app(SomeClass::class, ['param' => 'fakeparam'])); this yields SomeClass instead of the mockery instance.

This seems like an unfortunate inconsistency, but maybe this functionality is intentional?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 34
  • Comments: 15 (3 by maintainers)

Most upvoted comments

In case someone else is looking for this. You can mock it like this in your tests. Use bind() instead of instance()

       $orders_mock = \Mockery::mock(OrdersRequest::class)->makePartial();
       $orders_mock->shouldReceive('send')->andReturn($order_response);
       $this->app->bind(OrdersRequest::class, function() use ($orders_mock){
           return $orders_mock;
       });

And it will use the Mock.

Hello,

it seems that i have the same issue in 5.8.16, in case someone find this issue via google like i did, i solved this by doing :

App::offsetSet(Class::class, $this->mock(Class::class, function ($mock) {
    // do whatever you want            
    $mock;
}));

until this issue is resolved (if it’s resolved at all)

I wonder then how I can mock a job that accepts parameters, because I currently cannot due to this issue.

Are there any news? It is really exhausting writing tests with this behavior. I’m using version 5.5.

In the original PR https://github.com/laravel/framework/pull/18271 - Taylor says:

In this version, it will never use a previously resolved singleton instance when calling makeWith. In my opinion it should make a new instance each time this method is called because the given parameter array is dynamic.

I’m wondering if this is a side effect of that…

…but it does seem slightly broken - because it means it is not fullable testable.

bind() also works as a replacement for $this->mock(). My comment from #25041:

I had been using $this->mock() in my tests, but it wasn’t properly mocking an app(ClassToMock::class. [‘param’ => $param]) instance. I switched to Mockery::mock() + bind() and the mocking worked as expected.

In case someone else is looking for this. You can mock it like this in your tests. Use bind() instead of instance()

       $orders_mock = \Mockery::mock(OrdersRequest::class)->makePartial();
       $orders_mock->shouldReceive('send')->andReturn($order_response);
       $this->app->bind(OrdersRequest::class, function() use ($orders_mock){
           return $orders_mock;
       });

And it will use the Mock.

This solved my issue.

Hello,

i’m having this issue again in 2023, the App::offsetSet trick from 2019 still works at least, but it would be great to have something like mockWith to be able to specity parameters

In case someone else is looking for this. You can mock it like this in your tests. Use bind() instead of instance()

       $orders_mock = \Mockery::mock(OrdersRequest::class)->makePartial();
       $orders_mock->shouldReceive('send')->andReturn($order_response);
       $this->app->bind(OrdersRequest::class, function() use ($orders_mock){
           return $orders_mock;
       });

And it will use the Mock.

This worked just perfectly.

Just don’t forget to instantiate object via App:make(YourObject::class, ['argument' => $argument]) instead of new YourObject($argument).

See also https://github.com/laravel/framework/issues/25041 for recent discussions.