is laravel cloning my mock object? - laravel

Im testing a Soap web service with getMockFromWsdl from phpunit, for unit testing within laravel works fine, but when I try to replace the SoapClient in a feature test, it always fails, like the web service never called, but actually the mock is called.
I suspect that laravel is cloning somewhere my $this->soapClient because if I debug the code, it calls the soap mock and gets what is faked in the mock but always receive the error:
Expectation failed for method name is equal to <string:GetToken> when invoked at least once.
Expected invocation at least once but it never occurred.
My code is like:
public function test_soap_call()
{
$this->soapClient = $this->getMockFromWsdl(dirname(__FILE__).'/../Mocks/service.wsdl');
$this->soapClient->expects($this->atLeastOnce())
->method('GetToken')
->with(['Code' => '03600', 'User' => 'username'])
->willReturn(unserialize('O:8:"stdClass":1:{s:26:"GetTokenResult";s:36:"5aae60ec-2bcd-459d-a135-a20eb7c10007";}'));
$this->app->instance('MySoapClient', $this->soapClient);
$this->postJson('/api/order', $this->getValidRequest());
}
and in my controller (/api/order) I have
$soap = $this->app->make('MySoapClient');
$soap->GetToken(['Code' => '03600', 'User' => 'username']);
Am I using correctly the Laravel Service Container?
PD: Something similar happened to me, when doing a Spy and using $app->instance, where I was trying to get what was passed to an object, but always got null. I solved it declaring the field of the spy static.

Check this from php.net:
http://php.net/manual/en/language.oop5.references.php
... "objects are passed by references by default". This is not completely true. ...

Related

WrappedAPIView.__name__ = func.__name__ . AttributeError: 'dict' object has no attribute '__name__'

I am in a little bit of a pickle.
I have multiple decorators wrapping my view functions. I want to test that view function using pytest and this means the decorators will also be executed. Now, in some of those decorators, I am making API calls to an external service and I do not want to make those API calls while running my test, what I am doing instead is to mock the response from those decorators. When I ran the test I got AttributeError: 'dict' object has no attribute '__name__' and pytest is pointing to the decorators.py file in the djangorestframework package as the source of the error. Any idea what I am doing wrong?
Views.py file
#api_view(['POST'])
#DecoratorClass.decorator_one
#DecoratorClass.decorator_two
#DecoratorClass.decorator_three
#DecoratorClass.decorator_four
#DecoratorClass.decorator_five
#DecoratorClass.decorator_six
#DecoratorClass.decorator_seven
def my_view_fun(request):
my_data = TenantService.create_tenant(request)
return ResponseManager.handle_response(message="sucessful", data=my_data.data, status=201)
This works perfectly with manual testing, I only get this problem when I am running the test with pytest.
I am making the external API calls in decorators three, four and five.
TL;DR:
How can I handle the decorators wrapped around a view function when testing that view function in a situation where some of those decorators are making external API calls which should ideally be mocked in a test.

Trouble faking a Laravel HttpClient response

I am trying to test the following bit of code:
DimonaClient is just a simple wrapper around a Laravel HttpClient; simplified function here:
The getDeclaration() response is a \Illuminate\Http\Client\Response
What I am trying to do in my test is:
Mock the DimonaClient class so I don't create an actual api call
"Mock" (use Laravel's Http::response()) the response I want so that I can test that a 200 w/ certain statuses dispatches the appropriate event (also mocked, but not relevant here)
My test code looks like this:
My issue(s) seem to be:
the getDeclaration() has an expectation of Illuminate\Http\Client\Response but I can't seem to create anything that will satisfy that (a new Response wants a MessageInterface, etc, etc... )
I don't actually need getDeclaration() to return anything for my testing, so I wonder if I should be mocking this differently in any case (I base this assumption on Http::response handling the internal code I'm testing for things like $response->ok(), instead of a Mockery expectation)
I feel like I'm one small step away from making this work, but going round in circles trying to hook it up correctly.
TIA!
If you are using Http Facade, you don't need to mock DimonaCient. You are nearly there with your test, but let me show you what you would have done:
/** #test */
public function it_can_handle_an_approved_submission(): void
{
Http::fake([
'*' => Http::response([
'declarationStatus' => [
'result' => DimonaDeclarationStatus::ACCEPTED,
'dimonaPeriodId' => $this->faker->numerify('############'),
],
],
]);
$dimonaDeclarationId = $this->faker->numerify('############');
// Do your normal call, and then assertions
}
Doing this, you will tell Http to fake any URL, because we are using *. I would recommend you use $this->endpoint/$declarationId so if it does not match, you will also know you did not hit the right endpoint.
I am not sure what Laravel you are using but this is available since Laravel 6+, check Http fake URLs.

How can I use cache tags whith a mocked Cache in phpunit tests?

I have implemented Cache tags on a laravel project and I have something like this in my controller :
if (Cache::tags(['api'])->has('someKey')) {
return new JsonResponse(Cache::tags(['api'])->get('someKey'));
}
I would like to write a phpunit test to test this code : I mocked the Cache like found in the laravel documentation, but I haven't found anything on how to use tags on a mocked cache
I tried :
Cache::tags(['api'])->shouldReceive('has')->with('someKey')->andReturn(true)->once();
or
Cache::shouldReceive('has')->tags(['api'])->with('someKey')->andReturn(true)->once();
But none of this works, I got Call to undefined method Illuminate\Cache\ArrayStore::shouldReceive() or
call_user_func_array() expects parameter 1 to be a valid callback, class 'Mockery\Expectation' does not have a method 'tags'
Does anybody have a clue ? thanks a lot :)
You will need to mock tags call by itself and use ->andReturnSelf() to be able to make the mock chain. So calling Cache::tags(['api']) returns the mock instance.
Cache::shouldReceive('tags')->with(['api'])->andReturnSelf();
Cache::shouldReceive('has')->once()->with('someKey')->andReturn(true);
Cache::shouldReceive('get')->once()->with('someKey')->andReturn('result');

How do I mock find method in laravel?

I am trying to write a unit test, and I need to be able to mock an internal call to App::make('ClassName')->find($x). However, when I try this:
$mock = $this->getMockBuilder('ClassName')->
setMethods(['find'])->
getMock();
$mock->method('find')->willReturn('test');
echo $mock->find(1);
I get a PHPUnit_Framework_MockObject_BadMethodCallException with no message describing the error. Looking ad xdebug trace, I see that it calls the mock::find method, then immediately calls spl_autoload_call to get the BadMethodCallException class.
Why is the find method failing? If I use a different method (e.g. findx), then it works perfectly. What is magic about find, and how can I fix it?

Meteor 0.5.9: replacement for using Session in a server method?

So, I was attempting to do something like the following:
if(Meteor.isServer){
Meteor.methods({connect_to_api: function(vars){
// get data from remote API
return data;
}});
}
if(Meteor.isClient){
Template.myTpl.content = function(){
Meteor.call('connect_to_api', vars, function(err,data){
Session.set('placeholder', data);
});
return Session.get('placeholder');
};
}
This seemed to be working fine, but, of course, now breaks in 0.5.9 as the Session object has been removed from the server. How in the world do you now create a reactive Template that uses a server-only (stuff we don't want loading on the client) method call and get data back from that Method call. You can't put any Session references in the callback function because it doesn't exist on the server, and I don't know of any other reactive data sources available for this scenario.
I'm pretty new to Meteor, so I'm really trying to pin down best-practices stuff that has the best chance of being future-proof. Apparently the above implementation was not it.
EDIT: To clarify, this is not a problem of when I'm returning from the Template function. This is a problem of Session existing on the server. The above code will generate the following error message on the server:
Exception while invoking method 'connect_to_api' ReferenceError: Session is not defined
at Meteor.methods.connect_to_api (path/to/file.js:#:#)
at _.extend.protocol_handlers.method.exception ... etc etc
Setting the session in the callback seems to work fine, see this project I created on github: https://github.com/jtblin/meteor_session_test. In this example, I return data in a server method, and set it in the session in the callback.
There are 2 issues with your code:
1) Missing closing brace placement in Meteor.methods. The code should be:
Meteor.methods({
connect_to_api: function(vars) {
// get data from remote API
return data;
}
});
2) As explained above, you return the value in the session, before the callback is completed, i.e. before the callback method had the time to set the session variable. I guess this is why you don't see any data in the session variable yet.
I feel like an idiot (not the first time, not the last). Thanks to jtblin for showing me that Session.set does indeed work in the callback, I went back and scoured my Meteor.method function. Turns out there was one spot buried in the code where I was using Session.get which was what was throwing the error. Once I passed that value in from the client rather than trying to get it in the method itself, all was right with the world.
Oh, and you can indeed order things as above without issue.

Resources