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?
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.
I wonder then how I can mock a job that accepts parameters, because I currently cannot due to this issue.
I have this problem too. The current behaviour seems like a bug to me. This issue helped me understand it.
So only way now in my project is to introduce a conditional for app()->environment() === 'testing'
. As well as introduce default values for the parameters.
Please reconsider this one :-)
I had to do a dirty trick to test this too. Would love to see this changed someway.
Are there any news? It is really exhausting writing tests with this behavior. I'm using version 5.5.
It appears that passing $parameters
in to the Container resolve method forces it to resolve contextually:
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
Simply deleting ! empty($parameters) ||
seems to fix the problem for me. I'm not familiar enough with Laravels inner workings to be sure if that is the proper fix though, or if it breaks anything else.
See also https://github.com/laravel/framework/issues/25041 for recent discussions.
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.
@mikepmtl even when you call $this->json('POST',....) ?! Example:
`$request = [.....];
$paypalPayment = Mockery::mock(
PayPalPayment::class,
[$request]
)->makePartial();
$object = (object)null;
$object->status = 'APPROVED';
$object->purchase_units[0]->amount['value'] = 15;
$object->purchase_units[0]->amount['currency_code'] = 'EUR';
$paypalPayment->shouldReceive('captureDetail')
->andReturn(json_decode(json_encode($object), true));
$paypalPayment->shouldReceive('capturePayment')
->andReturn([
'status' => 'COMPLETED',
]);
$this->app->bind(PayPalPayment::class, function () use ($paypalPayment) {
return $paypalPayment;
});
$this->json(
'POST',
"{$this->baseUrl}/{$this->version}/orders/pay",
$request,
[
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]
)->assertJsonFragment([
'data' => [
'method' => 'paypal',
'status' => 'SUCCESS',
'response' => [
'status' => 'COMPLETED',
],
],
])->assertStatus(HttpStatusCodes::OK);`
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 have kind of similar problem here, https://stackoverflow.com/q/59853235, but none of the solutions here works. Any idea?
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)
.
Most helpful comment
In case someone else is looking for this. You can mock it like this in your tests.
Use bind() instead of instance()
And it will use the Mock.