WrappedAPIView.__name__ = func.__name__ . AttributeError: 'dict' object has no attribute '__name__' - django-rest-framework

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.

Related

Django rest framework question: Using TestCase I'm not understanding using fixtures

I'm confused about factories.
#pytest.fixture
def a_api_request_factory():
return APIRequestFactory()
class TestUserProfileDetailView(TestCase):
def test_create_userprofile(self, up=a_user_profile, rf=a_api_request_factory):
"""creates an APIRequest and uses an instance of UserProfile from a_user_profile to test a view user_detail_view"""
request = rf().get('/api/userprofile/') # the problem line
request.user = up.user
response = userprofile_detail_view(request)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['user'], up.user.username)
if I take out the parens from rf().get.... then I get
"function doesn't have a get attribute".
If I call it directly then it gives me:
"Fixture "a_api_request_factory" called directly. Fixtures are not
meant to be called directly, but are created automatically when test
functions request them as parameters. See
https://docs.pytest.org/en/stable/fixture.html for more information
about fixtures, and
https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly
about how to update your code."
I do believe I've hit every combination of with or without parens in all relevant locations. Where do the parens go for fixtures?
Or better yet is there a pattern to avoid this type of confusion completely?

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');

is laravel cloning my mock object?

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. ...

Typescript syntax - How to spy on an Ajax request?

I am trying to write a unit test where I want to verify that a ajax call has been made.
The code is simple :
it('test spycall',()=>{
spyOn($,"ajax");
//my method call which in turns use ajax
MyFunc();
expect($.ajax.calls.mostRecent().args[0]["url"].toEqual("myurl");
});
The error that I get :
Property 'calls' doesn't exist on type '{settings:jqueryAjaxSettings):jQueryXHR;(url:string, settings?:JQueryAjaxSettings}
$.ajax.calls, among others, is part of the Jasmine testing framework, not JQuery itself (As you know, Jasmine (or rather, Jasmine-Jquery, the plugin you're using) is adding certain debugging functions to JQuery's prototype in order to, well, be able to test ajax calls).
The bad part is that your .d.ts typescript definition file, the file that acts as an interface between typescript and pure JS libraries isn't aware of Jasmine's functions.
There are several ways you could approach fixing this, like
looking if someone has adjusted the JQuery .d.ts file for Jasmine's functions or
creating the new .d.ts file yourself by modifying the original one or, (what I would be doing)
overwriting the typescript definition by declaring $.ajax as any, or not including the typescript definition at all in your testing codebase and declaring $ as any.
There are 2 ways to get rid of the error:
// 1
expect(($.ajax as any).calls.mostRecent().args[0].url).toEqual("myurl");
// 2
let ajaxSpy = spyOn($,"ajax");
expect(ajaxSpy.calls.mostRecent().args[0].url).toEqual("myurl");
You can also use partial matching:
expect(($.ajax as any).calls.mostRecent().args).toEqual([
jasmine.objectContaining({url: "myurl"})
]);

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?

Resources