Reputation: 39
Let's assume that I have the following service object:
class Foo(object):
def bar(self):
return ['foo', 'bar']
And this is the schema:
import Foo
class Query(graphene.ObjectType):
bar = graphene.List(lambda: graphene.String)
def resolve_bar(self, info):
return Foo().bar()
I am trying to test if the GraphQL Schema is correctly calling the method bar
in its query resolver. So, we have this piece of code in the tests:
from MySchema import Query
class TestFooBar(unittest.TestCase):
@patch('Foo')
def test_bar(self, mock_foo):
mock_foo.return_value.bar.return_value = ['baz', 'qux']
my_schema = graphene.Schema(query=Query)
client = Client(self.my_schema)
query = '''
query {
bar()
}
'''
executed = self.client.execute(query)
#some attributes that I want to assert
assertTrue(mock_foo.called) # returns False
In the original ServiceObject class it makes some API calls to another service, which is already tested in a isolated way. And in this case, I only want to test if the GraphQL query bar
is calling a method which will return its supposed object.
When I mock the response that the service object returns as in the above code, and make the Graphene Client test runs the query, it gives me the 'non-mocked' response. In other words, it is actually calling the original method provided by the service object class and making the API calls, which shouldn't be performed. However, when I instantiate and run the ServiceObject class itself, it is correctly mocked and returns the ['baz', 'qux']
array, not going through the API calls.
Or GraphQL Client responses shouldn't be mocked at all? Is there any approach that I can use instead of mocking it?
I already looked all over internet to see how people do that, but I couldn't manage to find any possible solutions.
Upvotes: 2
Views: 2908
Reputation: 177
I know that this question is about Graphene, but I had the same problem (mocking graphql resolver) with Ariadne and write this solution for others like me who have no access to enough documents about graphql unit tests with ariadne.
As said in the accepted answer, mocking resolver is not enough and graphql still calls the not mocked resolver. So we must change schema to call this new function instead of main resolver. And -in my case- change django url to use new executable schema
from .urls import urlpatterns
with patch('path.to.my_resolver') as mocked_resolver:
query = QueryType()
query.set_field(mocked_field, mocked_resolver)
mocked_resolver.return_value = mocked_return_value
my_schema = make_executable_schema(my_base_schema, query, snake_case_fallback_resolvers)
urlpatterns.clear() # remove main urls
urlpatterns.append(path('graphql/', GraphQLView.as_view(schema=my_schema), name='graphql'))
Note: snake_case_fallback_resolvers is because of handling convert_kwargs_to_snake_case decorator
Upvotes: 0
Reputation: 39
My patch
is wrong. I should patch the callsite, not the definition site. In this case, it will be: @patch('MySchema.Foo')
to accomplish the mocking of the callsite.
from MySchema import Query
class TestFooBar(unittest.TestCase):
@patch('MySchema.Foo')
def test_bar(self, mock_foo):
mock_foo.return_value.bar.return_value = ['baz', 'qux']
my_schema = graphene.Schema(query=Query)
client = Client(self.my_schema)
query = '''
query {
bar()
}
'''
executed = self.client.execute(query)
assertTrue(mock_foo().bar.called) # now returns True
Thanks to jkimbo who came on the rescue when I asked in the Graphene-Python Github repository.
Upvotes: 1