Repository: saleor/saleor

Issue:
API tests don't test permissions
### What I'm trying to achieve
Tests suite should be precise. Now all non-user actions are tested using `admin_api_client` fixture which operates on superuser privileges level. This way, we don't test if staff API endpoints contain correct permissions checking.
In the future, it may lead to a potential security issue.

### What I expected to happen
Staff API endpoints tests should use `staff_api_client` fixture and add correct permissions at the beginning of the test. Insufficient permissions test case should be covered as well.



Discussion:

Resolving patch:
diff --git a/saleor/graphql/account/mutations.py b/saleor/graphql/account/mutations.py
--- a/saleor/graphql/account/mutations.py
+++ b/saleor/graphql/account/mutations.py
@@ -229,7 +229,7 @@ class Meta:
 
     @classmethod
     def user_is_allowed(cls, user, input):
-        return user.is_staff
+        return user.has_perm('account.manage_staff')
 
     @classmethod
     def clean_input(cls, info, instance, input, errors):
diff --git a/saleor/graphql/api.py b/saleor/graphql/api.py
--- a/saleor/graphql/api.py
+++ b/saleor/graphql/api.py
@@ -258,9 +258,11 @@ def resolve_voucher(self, info, id):
     def resolve_vouchers(self, info, query=None, **kwargs):
         return resolve_vouchers(info, query)
 
+    @permission_required('shipping.manage_shipping')
     def resolve_shipping_zone(self, info, id):
         return graphene.Node.get_node_from_global_id(info, id, ShippingZone)
 
+    @permission_required('shipping.manage_shipping')
     def resolve_shipping_zones(self, info, **kwargs):
         return resolve_shipping_zones(info)
 
diff --git a/saleor/graphql/product/mutations/attributes.py b/saleor/graphql/product/mutations/attributes.py
--- a/saleor/graphql/product/mutations/attributes.py
+++ b/saleor/graphql/product/mutations/attributes.py
@@ -36,6 +36,10 @@ class Meta:
         description = 'Creates an attribute.'
         model = models.Attribute
 
+    @classmethod
+    def user_is_allowed(cls, user, input):
+        return user.has_perm('product.manage_products')
+
     @classmethod
     def clean_attribute_value_uniqueness(cls, values, errors, error_msg):
         """Checks if all provided values are unique."""


Test patch:
diff --git a/tests/api/conftest.py b/tests/api/conftest.py
new file mode 100644
--- /dev/null
+++ b/tests/api/conftest.py
@@ -0,0 +1,81 @@
+import json
+
+import pytest
+
+from django.shortcuts import reverse
+from django.test.client import MULTIPART_CONTENT, Client
+from graphql_jwt.shortcuts import get_token
+
+from .utils import assert_no_permission
+
+API_PATH = reverse('api')
+
+
+class ApiClient(Client):
+    """GraphQL API client."""
+
+    def __init__(self, *args, **kwargs):
+        user = kwargs.pop('user')
+        self.user = user
+        self.token = get_token(user)
+        super().__init__(*args, **kwargs)
+
+    def _base_environ(self, **request):
+        environ = super()._base_environ(**request)
+        environ.update({'HTTP_AUTHORIZATION': 'JWT %s' % self.token})
+        return environ
+
+    def post(self, data=None, **kwargs):
+        """Send a POST request.
+
+        This wrapper sets the `application/json` content type which is
+        more suitable for standard GraphQL requests and doesn't mismatch with
+        handling multipart requests in Graphene.
+        """
+        if data:
+            data = json.dumps(data)
+        kwargs['content_type'] = 'application/json'
+        return super().post(API_PATH, data, **kwargs)
+
+    def post_graphql(self, query, variables=None, permissions=None, **kwargs):
+        """Dedicated helper for posting GraphQL queries.
+
+        Sets the `application/json` content type and json.dumps the variables
+        if present.
+        """
+        data = {'query': query}
+        if variables is not None:
+            data['variables'] = json.dumps(variables)
+        if data:
+            data = json.dumps(data)
+        kwargs['content_type'] = 'application/json'
+
+        if permissions:
+            response = super().post(API_PATH, data, **kwargs)
+            assert_no_permission(response)
+            self.user.user_permissions.add(*permissions)
+        return super().post(API_PATH, data, **kwargs)
+
+    def post_multipart(self, *args, permissions=None, **kwargs):
+        """Send a multipart POST request.
+
+        This is used to send multipart requests to GraphQL API when e.g.
+        uploading files.
+        """
+        kwargs['content_type'] = MULTIPART_CONTENT
+
+        if permissions:
+            response = super().post(API_PATH, *args, **kwargs)
+            assert_no_permission(response)
+            self.user.user_permissions.add(*permissions)
+        return super().post(API_PATH, *args, **kwargs)
+
+
+@pytest.fixture
+def staff_api_client(staff_user):
+    return ApiClient(user=staff_user)
+
+
+@pytest.fixture
+def user_api_client(customer_user):
+    return ApiClient(user=customer_user)
diff --git a/tests/api/test_account.py b/tests/api/test_account.py
--- a/tests/api/test_account.py
+++ b/tests/api/test_account.py
@@ -84,7 +84,7 @@ def test_token_create_user_data(
     assert token_data['user']['permissions'][0]['code'] == code
 
 
-def test_query_user(admin_api_client, customer_user):
+def test_query_user(staff_api_client, customer_user, permission_manage_users):
     user = customer_user
     query = """
     query User($id: ID!) {
@@ -118,7 +118,8 @@ def test_query_user(admin_api_client, customer_user):
     """
     ID = graphene.Node.to_global_id('User', customer_user.id)
     variables = {'id': ID}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     data = content['data']['user']
     assert data['email'] == user.email
@@ -141,7 +142,8 @@ def test_query_user(admin_api_client, customer_user):
     assert address['phone'] == user_address.phone.as_e164
 
 
-def test_query_customers(admin_api_client, user_api_client):
+def test_query_customers(
+        staff_api_client, user_api_client, permission_manage_users):
     query = """
     query Users {
         customers {
@@ -155,7 +157,8 @@ def test_query_customers(admin_api_client, user_api_client):
     }
     """
     variables = {}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     users = content['data']['customers']['edges']
     assert users
@@ -167,8 +170,8 @@ def test_query_customers(admin_api_client, user_api_client):
 
 
 def test_query_staff(
-        admin_api_client, user_api_client, staff_user, customer_user,
-        admin_user):
+        staff_api_client, user_api_client, staff_user, customer_user,
+        admin_user, permission_manage_staff):
     query = """
     {
         staffUsers {
@@ -182,7 +185,8 @@ def test_query_staff(
     }
     """
     variables = {}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_staff])
     content = get_graphql_content(response)
     data = content['data']['staffUsers']['edges']
     assert len(data) == 2
@@ -267,7 +271,8 @@ def test_customer_register(user_api_client):
 
 @patch('saleor.account.emails.send_password_reset_email.delay')
 def test_customer_create(
-        send_password_reset_mock, admin_api_client, user_api_client, address):
+        send_password_reset_mock, staff_api_client, user_api_client, address,
+        permission_manage_users):
     query = """
     mutation CreateCustomer(
         $email: String, $note: String, $billing: AddressInput,
@@ -310,10 +315,8 @@ def test_customer_create(
         'billing': address_data,
         'send_mail': True}
 
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
 
     User = get_user_model()
@@ -339,7 +342,8 @@ def test_customer_create(
 
 
 def test_customer_update(
-        admin_api_client, customer_user, user_api_client, address):
+        staff_api_client, customer_user, user_api_client, address,
+        permission_manage_users):
     query = """
     mutation UpdateCustomer($id: ID!, $note: String, $billing: AddressInput, $shipping: AddressInput) {
         customerUpdate(id: $id, input: {
@@ -384,12 +388,8 @@ def test_customer_update(
         'note': note,
         'billing': address_data,
         'shipping': address_data}
-
-    # check unauthorized access
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
 
     User = get_user_model()
@@ -409,8 +409,8 @@ def test_customer_update(
 
 @patch('saleor.account.emails.send_password_reset_email.delay')
 def test_staff_create(
-        send_password_reset_mock, admin_api_client, user_api_client,
-        permission_manage_users, permission_manage_products, staff_user):
+        send_password_reset_mock, staff_api_client,
+        permission_manage_staff, permission_manage_products, staff_user):
     query = """
     mutation CreateStaff($email: String, $permissions: [String], $send_mail: Boolean) {
         staffCreate(input: {email: $email, permissions: $permissions, sendPasswordEmail: $send_mail}) {
@@ -436,17 +436,13 @@ def test_staff_create(
         permission_manage_products.codename)
 
     email = 'api_user@example.com'
-    staff_user.user_permissions.add(permission_manage_users)
     variables = {
         'email': email,
         'permissions': [permission_manage_products_codename],
         'send_mail': True}
 
-    # check unauthorized access
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_staff])
     content = get_graphql_content(response)
     data = content['data']['staffCreate']
     assert data['errors'] == []
@@ -464,7 +460,7 @@ def test_staff_create(
     assert 'token' in call_context
 
 
-def test_staff_update(admin_api_client, staff_user, user_api_client):
+def test_staff_update(staff_api_client, permission_manage_staff):
     query = """
     mutation UpdateStaff(
             $id: ID!, $permissions: [String], $is_active: Boolean) {
@@ -484,14 +480,13 @@ def test_staff_update(admin_api_client, staff_user, user_api_client):
         }
     }
     """
+    staff_user = User.objects.create(
+        email='staffuser@example.com', is_staff=True)
     id = graphene.Node.to_global_id('User', staff_user.id)
     variables = {'id': id, 'permissions': [], 'is_active': False}
 
-    # check unauthorized access
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_staff])
     content = get_graphql_content(response)
     data = content['data']['staffUpdate']
     assert data['errors'] == []
@@ -499,7 +494,7 @@ def test_staff_update(admin_api_client, staff_user, user_api_client):
     assert data['user']['isActive'] == False
 
 
-def test_staff_delete(admin_api_client, staff_user, user_api_client):
+def test_staff_delete(staff_api_client, permission_manage_staff):
     query = """
         mutation DeleteStaff($id: ID!) {
             staffDelete(id: $id) {
@@ -513,14 +508,13 @@ def test_staff_delete(admin_api_client, staff_user, user_api_client):
             }
         }
     """
+    staff_user = User.objects.create(
+        email='staffuser@example.com', is_staff=True)
     user_id = graphene.Node.to_global_id('User', staff_user.id)
     variables = {'id': user_id}
 
-    # check unauthorized access
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_staff])
     content = get_graphql_content(response)
     data = content['data']['staffDelete']
     assert data['errors'] == []
@@ -597,7 +591,8 @@ def test_set_password(user_api_client, customer_user):
 
 @patch('saleor.account.emails.send_password_reset_email.delay')
 def test_password_reset_email(
-        send_password_reset_mock, admin_api_client, customer_user):
+        send_password_reset_mock, staff_api_client, customer_user,
+        permission_manage_users):
     query = """
     mutation ResetPassword($email: String!) {
         passwordReset(email: $email) {
@@ -610,7 +605,8 @@ def test_password_reset_email(
     """
     email = customer_user.email
     variables = {'email': email}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     data = content['data']['passwordReset']
     assert data is None
@@ -624,7 +620,8 @@ def test_password_reset_email(
 
 @patch('saleor.account.emails.send_password_reset_email.delay')
 def test_password_reset_email_non_existing_user(
-        send_password_reset_mock, admin_api_client, customer_user):
+        send_password_reset_mock, staff_api_client, customer_user,
+        permission_manage_users):
     query = """
     mutation ResetPassword($email: String!) {
         passwordReset(email: $email) {
@@ -637,7 +634,8 @@ def test_password_reset_email_non_existing_user(
     """
     email = 'not_exists@example.com'
     variables = {'email': email}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     data = content['data']['passwordReset']
     assert data['errors'] == [{
@@ -646,7 +644,8 @@ def test_password_reset_email_non_existing_user(
     send_password_reset_mock.assert_not_called()
 
 
-def test_create_address_mutation(admin_api_client, customer_user):
+def test_create_address_mutation(
+        staff_api_client, customer_user, permission_manage_users):
     query = """
     mutation CreateUserAddress($user: ID!, $city: String!, $country: String!) {
         addressCreate(input: {userId: $user, city: $city, country: $country}) {
@@ -666,7 +665,8 @@ def test_create_address_mutation(admin_api_client, customer_user):
     """
     user_id = graphene.Node.to_global_id('User', customer_user.id)
     variables = {'user': user_id, 'city': 'Dummy', 'country': 'PL'}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     assert content['data']['addressCreate']['errors'] == []
     address_response = content['data']['addressCreate']['address']
@@ -676,7 +676,8 @@ def test_create_address_mutation(admin_api_client, customer_user):
     assert address_obj.user_addresses.first() == customer_user
 
 
-def test_address_update_mutation(admin_api_client, customer_user):
+def test_address_update_mutation(
+        staff_api_client, customer_user, permission_manage_users):
     query = """
     mutation updateUserAddress($addressId: ID!, $city: String!) {
         addressUpdate(id: $addressId, input: {city: $city}) {
@@ -691,7 +692,8 @@ def test_address_update_mutation(admin_api_client, customer_user):
     variables = {
         'addressId': graphene.Node.to_global_id('Address', address_obj.id),
         'city': new_city}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     data = content['data']['addressUpdate']
     assert data['address']['city'] == new_city
@@ -699,7 +701,8 @@ def test_address_update_mutation(admin_api_client, customer_user):
     assert address_obj.city == new_city
 
 
-def test_address_delete_mutation(admin_api_client, customer_user):
+def test_address_delete_mutation(
+        staff_api_client, customer_user, permission_manage_users):
     query = """
             mutation deleteUserAddress($id: ID!) {
                 addressDelete(id: $id) {
@@ -711,7 +714,8 @@ def test_address_delete_mutation(admin_api_client, customer_user):
         """
     address_obj = customer_user.addresses.first()
     variables = {'id': graphene.Node.to_global_id('Address', address_obj.id)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_users])
     content = get_graphql_content(response)
     data = content['data']['addressDelete']
     assert data['address']['city'] == address_obj.city
diff --git a/tests/api/test_attributes.py b/tests/api/test_attributes.py
--- a/tests/api/test_attributes.py
+++ b/tests/api/test_attributes.py
@@ -109,13 +109,15 @@ def test_attributes_in_category_query(user_api_client, product):
 """
 
 
-def test_create_attribute_and_attribute_values(admin_api_client):
+def test_create_attribute_and_attribute_values(
+        staff_api_client, permission_manage_products):
     query = CREATE_ATTRIBUTES_QUERY
     attribute_name = 'Example name'
     name = 'Value name'
     variables = {
         'name': 'Example name', 'values': [{'name': name, 'value': '#1231'}]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert not content['data']['attributeCreate']['errors']
     data = content['data']['attributeCreate']['attribute']
@@ -136,14 +138,16 @@ def test_create_attribute_and_attribute_values(admin_api_client):
             'Red color', 'red color',
             'Provided AttributeValue names are not unique.')))
 def test_create_attribute_and_attribute_values_errors(
-        admin_api_client, name_1, name_2, error_msg):
+        staff_api_client, name_1, name_2, error_msg,
+        permission_manage_products):
     query = CREATE_ATTRIBUTES_QUERY
     variables = {
         'name': 'Example name',
         'values': [
             {'name': name_1, 'value': '#1231'},
             {'name': name_2, 'value': '#121'}]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     errors = content['data']['attributeCreate']['errors']
     assert errors
@@ -177,13 +181,15 @@ def test_create_attribute_and_attribute_values_errors(
 """
 
 
-def test_update_attribute_name(admin_api_client, color_attribute):
+def test_update_attribute_name(
+        staff_api_client, color_attribute, permission_manage_products):
     query = UPDATE_ATTRIBUTE_QUERY
     attribute = color_attribute
     name = 'Wings name'
     id = graphene.Node.to_global_id('Attribute', attribute.id)
     variables = {'name': name, 'id': id, 'addValues': [], 'removeValues': []}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     attribute.refresh_from_db()
     data = content['data']['attributeUpdate']
@@ -191,7 +197,7 @@ def test_update_attribute_name(admin_api_client, color_attribute):
 
 
 def test_update_attribute_remove_and_add_values(
-        admin_api_client, color_attribute):
+        staff_api_client, color_attribute, permission_manage_products):
     query = UPDATE_ATTRIBUTE_QUERY
     attribute = color_attribute
     name = 'Wings name'
@@ -204,7 +210,8 @@ def test_update_attribute_remove_and_add_values(
         'name': name, 'id': id,
         'addValues': [{'name': attribute_value_name, 'value': '#1231'}],
         'removeValues': [value_id]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     attribute.refresh_from_db()
     data = content['data']['attributeUpdate']
@@ -223,7 +230,8 @@ def test_update_attribute_remove_and_add_values(
             'Red color', 'red color',
             'Provided AttributeValue names are not unique.')))
 def test_update_attribute_and_add_attribute_values_errors(
-        admin_api_client, name_1, name_2, error_msg, color_attribute):
+        staff_api_client, name_1, name_2, error_msg, color_attribute,
+        permission_manage_products):
     query = UPDATE_ATTRIBUTE_QUERY
     attribute = color_attribute
     id = graphene.Node.to_global_id('Attribute', attribute.id)
@@ -231,7 +239,8 @@ def test_update_attribute_and_add_attribute_values_errors(
         'name': 'Example name', 'id': id, 'removeValues': [],
         'addValues': [
             {'name': name_1, 'value': '#1'}, {'name': name_2, 'value': '#2'}]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     errors = content['data']['attributeUpdate']['errors']
     assert errors
@@ -240,7 +249,8 @@ def test_update_attribute_and_add_attribute_values_errors(
 
 
 def test_update_attribute_and_remove_others_attribute_value(
-        admin_api_client, color_attribute, size_attribute):
+        staff_api_client, color_attribute, size_attribute,
+        permission_manage_products):
     query = UPDATE_ATTRIBUTE_QUERY
     attribute = color_attribute
     id = graphene.Node.to_global_id('Attribute', attribute.id)
@@ -250,7 +260,8 @@ def test_update_attribute_and_remove_others_attribute_value(
     variables = {
         'name': 'Example name', 'id': id, 'slug': 'example-slug',
         'addValues': [], 'removeValues': [attr_id]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     errors = content['data']['attributeUpdate']['errors']
     assert errors
@@ -260,7 +271,8 @@ def test_update_attribute_and_remove_others_attribute_value(
         'AttributeValue does not belong to this Attribute.')
 
 
-def test_delete_attribute(admin_api_client, color_attribute):
+def test_delete_attribute(
+        staff_api_client, color_attribute, permission_manage_products):
     attribute = color_attribute
     query = """
     mutation deleteAttribute($id: ID!) {
@@ -273,7 +285,8 @@ def test_delete_attribute(admin_api_client, color_attribute):
     """
     id = graphene.Node.to_global_id('Attribute', attribute.id)
     variables = {'id': id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     with pytest.raises(attribute._meta.model.DoesNotExist):
         attribute.refresh_from_db()
@@ -304,14 +317,16 @@ def test_delete_attribute(admin_api_client, color_attribute):
 """
 
 
-def test_create_attribute_value(admin_api_client, color_attribute):
+def test_create_attribute_value(
+        staff_api_client, color_attribute, permission_manage_products):
     attribute = color_attribute
     query = CREATE_ATTRIBUTE_VALUE_QUERY
     attribute_id = graphene.Node.to_global_id('Attribute', attribute.id)
     name = 'test name'
     value = 'test-string'
     variables = {'name': name, 'value': value, 'id': attribute_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['attributeValueCreate']
     assert not data['errors']
@@ -325,14 +340,15 @@ def test_create_attribute_value(admin_api_client, color_attribute):
 
 
 def test_create_attribute_value_not_unique_name(
-        admin_api_client, color_attribute):
+        staff_api_client, color_attribute, permission_manage_products):
     attribute = color_attribute
     query = CREATE_ATTRIBUTE_VALUE_QUERY
     attribute_id = graphene.Node.to_global_id('Attribute', attribute.id)
     value_name = attribute.values.first().name
     variables = {
         'name': value_name, 'value': 'test-string', 'id': attribute_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['attributeValueCreate']
     assert data['errors']
@@ -364,13 +380,15 @@ def test_create_attribute_value_not_unique_name(
 """
 
 
-def test_update_attribute_value(admin_api_client, pink_attribute_value):
+def test_update_attribute_value(
+        staff_api_client, pink_attribute_value, permission_manage_products):
     query = UPDATE_ATTRIBUTE_VALUE_QUERY
     value = pink_attribute_value
     id = graphene.Node.to_global_id('AttributeValue', value.id)
     name = 'Crimson name'
     variables = {'name': name, 'value': '#RED', 'id': id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['attributeValueUpdate']
     value.refresh_from_db()
@@ -380,13 +398,14 @@ def test_update_attribute_value(admin_api_client, pink_attribute_value):
 
 
 def test_update_attribute_value_name_not_unique(
-        admin_api_client, pink_attribute_value):
+        staff_api_client, pink_attribute_value, permission_manage_products):
     query = UPDATE_ATTRIBUTE_VALUE_QUERY
     value = pink_attribute_value.attribute.values.create(
         name='Example Name', slug='example-name', value='#RED')
     id = graphene.Node.to_global_id('AttributeValue', value.id)
     variables = {'name': pink_attribute_value.name, 'value': '#RED', 'id': id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['attributeValueUpdate']
     assert data['errors']
@@ -395,13 +414,14 @@ def test_update_attribute_value_name_not_unique(
 
 
 def test_update_same_attribute_value(
-        admin_api_client, pink_attribute_value):
+        staff_api_client, pink_attribute_value, permission_manage_products):
     query = UPDATE_ATTRIBUTE_VALUE_QUERY
     value = pink_attribute_value
     id = graphene.Node.to_global_id('AttributeValue', value.id)
     attr_value = '#BLUE'
     variables = {'name': value.name, 'value': attr_value, 'id': id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['attributeValueUpdate']
     assert not data['errors']
@@ -409,7 +429,8 @@ def test_update_same_attribute_value(
 
 
 def test_delete_attribute_value(
-        admin_api_client, color_attribute, pink_attribute_value):
+        staff_api_client, color_attribute, pink_attribute_value,
+        permission_manage_products):
     value = pink_attribute_value
     value = color_attribute.values.get(name='Red')
     query = """
@@ -424,7 +445,8 @@ def test_delete_attribute_value(
     """
     id = graphene.Node.to_global_id('AttributeValue', value.id)
     variables = {'id': id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     with pytest.raises(value._meta.model.DoesNotExist):
         value.refresh_from_db()
 
diff --git a/tests/api/test_category.py b/tests/api/test_category.py
--- a/tests/api/test_category.py
+++ b/tests/api/test_category.py
@@ -43,7 +43,8 @@ def test_category_query(user_api_client, product):
         category.get_children().count())
 
 
-def test_category_create_mutation(admin_api_client):
+def test_category_create_mutation(
+        staff_api_client, permission_manage_products):
     query = """
         mutation($name: String, $slug: String, $description: String, $parentId: ID) {
             categoryCreate(
@@ -80,7 +81,8 @@ def test_category_create_mutation(admin_api_client):
     variables = {
         'name': category_name, 'description': category_description,
         'slug': category_slug}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['categoryCreate']
     assert data['errors'] == []
@@ -93,14 +95,15 @@ def test_category_create_mutation(admin_api_client):
     variables = {
         'name': category_name, 'description': category_description,
         'parentId': parent_id, 'slug': category_slug}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(query, variables)
     content = get_graphql_content(response)
     data = content['data']['categoryCreate']
     assert data['errors'] == []
     assert data['category']['parent']['id'] == parent_id
 
 
-def test_category_update_mutation(admin_api_client, category):
+def test_category_update_mutation(
+        staff_api_client, category, permission_manage_products):
     query = """
         mutation($id: ID!, $name: String, $slug: String, $description: String) {
             categoryUpdate(
@@ -138,7 +141,8 @@ def test_category_update_mutation(admin_api_client, category):
     variables = {
         'name': category_name, 'description': category_description,
         'id': category_id, 'slug': category_slug}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['categoryUpdate']
     assert data['errors'] == []
@@ -150,7 +154,8 @@ def test_category_update_mutation(admin_api_client, category):
     assert data['category']['parent']['id'] == parent_id
 
 
-def test_category_delete_mutation(admin_api_client, category):
+def test_category_delete_mutation(
+        staff_api_client, category, permission_manage_products):
     query = """
         mutation($id: ID!) {
             categoryDelete(id: $id) {
@@ -165,7 +170,8 @@ def test_category_delete_mutation(admin_api_client, category):
         }
     """
     variables = {'id': graphene.Node.to_global_id('Category', category.id)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['categoryDelete']
     assert data['category']['name'] == category.name
diff --git a/tests/api/test_core.py b/tests/api/test_core.py
--- a/tests/api/test_core.py
+++ b/tests/api/test_core.py
@@ -18,7 +18,8 @@ def test_clean_seo_fields():
     assert data['seo_description'] == description
 
 
-def test_user_error_field_name_for_related_object(admin_api_client):
+def test_user_error_field_name_for_related_object(
+        staff_api_client, permission_manage_products):
     query = """
     mutation {
         categoryCreate(input: {name: "Test", parent: "123456"}) {
@@ -32,7 +33,8 @@ def test_user_error_field_name_for_related_object(admin_api_client):
         }
     }
     """
-    response = admin_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['categoryCreate']['category']
     assert data is None
@@ -56,7 +58,8 @@ def test_snake_to_camel_case():
     assert snake_to_camel_case(123) == 123
 
 
-def test_mutation_returns_error_field_in_camel_case(admin_api_client, variant):
+def test_mutation_returns_error_field_in_camel_case(
+        staff_api_client, variant, permission_manage_products):
     # costPrice is snake case variable (cost_price) in the backend
     query = """
     mutation testCamel($id: ID!, $cost: Decimal) {
@@ -75,7 +78,8 @@ def test_mutation_returns_error_field_in_camel_case(admin_api_client, variant):
     variables = {
         'id': graphene.Node.to_global_id('ProductVariant', variant.id),
         'cost': 12.1234}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     error = content['data']['productVariantUpdate']['errors'][0]
     assert error['field'] == 'costPrice'
diff --git a/tests/api/test_discount.py b/tests/api/test_discount.py
--- a/tests/api/test_discount.py
+++ b/tests/api/test_discount.py
@@ -6,38 +6,10 @@
     DiscountValueTypeEnum, VoucherTypeEnum)
 from tests.api.utils import get_graphql_content
 
-from .utils import assert_no_permission
 
-
-def test_voucher_permissions(
-        staff_api_client, staff_user, permission_manage_discounts):
+def test_voucher_query(staff_api_client, voucher, permission_manage_discounts):
     query = """
-    query vouchers{
-        vouchers(first: 1) {
-            edges {
-                node {
-                    name
-                }
-            }
-        }
-    }
-    """
-    # Query to ensure user with no permissions can't see vouchers
-    response = staff_api_client.post_graphql(query)
-    assert_no_permission(response)
-
-    # Give staff user proper permissions
-    staff_user.user_permissions.add(permission_manage_discounts)
-
-    # Query again
-    response = staff_api_client.post_graphql(query)
-    get_graphql_content(response)
-
-
-def test_voucher_query(
-        admin_api_client, voucher):
-    query = """
-    query vouchers{
+    query vouchers {
         vouchers(first: 1) {
             edges {
                 node {
@@ -54,7 +26,8 @@ def test_voucher_query(
         }
     }
     """
-    response = admin_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['vouchers']['edges'][0]['node']
     assert data['type'] == voucher.type.upper()
@@ -67,10 +40,9 @@ def test_voucher_query(
     assert data['discountValue'] == voucher.discount_value
 
 
-def test_sale_query(
-        admin_api_client, sale):
+def test_sale_query(staff_api_client, sale, permission_manage_discounts):
     query = """
-        query sales{
+        query sales {
             sales(first: 1) {
                 edges {
                     node {
@@ -83,7 +55,8 @@ def test_sale_query(
             }
         }
         """
-    response = admin_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['sales']['edges'][0]['node']
     assert data['type'] == sale.type.upper()
@@ -92,7 +65,7 @@ def test_sale_query(
     assert data['startDate'] == sale.start_date.isoformat()
 
 
-def test_create_voucher(user_api_client, admin_api_client):
+def test_create_voucher(staff_api_client, permission_manage_discounts):
     query = """
     mutation  voucherCreate(
         $type: VoucherTypeEnum, $name: String, $code: String,
@@ -125,10 +98,9 @@ def test_create_voucher(user_api_client, admin_api_client):
         'discountValueType': DiscountValueTypeEnum.FIXED.name,
         'discountValue': 10.12,
         'minAmountSpent': 1.12}
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
 
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['voucherCreate']['voucher']
     assert data['type'] == VoucherType.VALUE.upper()
@@ -138,7 +110,8 @@ def test_create_voucher(user_api_client, admin_api_client):
     assert data['discountValueType'] == DiscountValueType.FIXED.upper()
 
 
-def test_update_voucher(user_api_client, admin_api_client, voucher):
+def test_update_voucher(
+        staff_api_client, voucher, permission_manage_discounts):
     query = """
     mutation  voucherUpdate($code: String,
         $discountValueType: DiscountValueTypeEnum, $id: ID!) {
@@ -164,17 +137,16 @@ def test_update_voucher(user_api_client, admin_api_client, voucher):
         'code': 'testcode123',
         'discountValueType': DiscountValueTypeEnum.PERCENTAGE.name}
 
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['voucherUpdate']['voucher']
     assert data['code'] == 'testcode123'
     assert data['discountValueType'] == DiscountValueType.PERCENTAGE.upper()
 
 
-def test_voucher_delete_mutation(user_api_client, admin_api_client, voucher):
+def test_voucher_delete_mutation(
+        staff_api_client, voucher, permission_manage_discounts):
     query = """
         mutation DeleteVoucher($id: ID!) {
             voucherDelete(id: $id) {
@@ -191,10 +163,8 @@ def test_voucher_delete_mutation(user_api_client, admin_api_client, voucher):
     """
     variables = {'id': graphene.Node.to_global_id('Voucher', voucher.id)}
 
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['voucherDelete']
     assert data['voucher']['name'] == voucher.name
@@ -202,7 +172,7 @@ def test_voucher_delete_mutation(user_api_client, admin_api_client, voucher):
         voucher.refresh_from_db()
 
 
-def test_create_sale(user_api_client, admin_api_client):
+def test_create_sale(staff_api_client, permission_manage_discounts):
     query = """
     mutation  saleCreate(
         $type: DiscountValueTypeEnum, $name: String, $value: Decimal) {
@@ -223,10 +193,8 @@ def test_create_sale(user_api_client, admin_api_client):
         'name': 'test sale',
         'type': DiscountValueTypeEnum.FIXED.name,
         'value': '10.12'}
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['saleCreate']['sale']
     assert data['type'] == DiscountValueType.FIXED.upper()
@@ -234,7 +202,7 @@ def test_create_sale(user_api_client, admin_api_client):
     assert data['value'] == 10.12
 
 
-def test_update_sale(user_api_client, admin_api_client, sale):
+def test_update_sale(staff_api_client, sale, permission_manage_discounts):
     query = """
     mutation  saleUpdate($type: DiscountValueTypeEnum, $id: ID!) {
             saleUpdate(id: $id, input: {type: $type}) {
@@ -255,17 +223,15 @@ def test_update_sale(user_api_client, admin_api_client, sale):
         'id': graphene.Node.to_global_id('Sale', sale.id),
         'type': DiscountValueTypeEnum.PERCENTAGE.name}
 
-    response = user_api_client.post_graphql(query, variables)
-
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['saleUpdate']['sale']
     assert data['type'] == DiscountValueType.PERCENTAGE.upper()
 
 
-def test_sale_delete_mutation(user_api_client, admin_api_client, sale):
+def test_sale_delete_mutation(
+        staff_api_client, sale, permission_manage_discounts):
     query = """
         mutation DeleteSale($id: ID!) {
             saleDelete(id: $id) {
@@ -282,10 +248,8 @@ def test_sale_delete_mutation(user_api_client, admin_api_client, sale):
     """
     variables = {'id': graphene.Node.to_global_id('Sale', sale.id)}
 
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_discounts])
     content = get_graphql_content(response)
     data = content['data']['saleDelete']
     assert data['sale']['name'] == sale.name
@@ -293,7 +257,8 @@ def test_sale_delete_mutation(user_api_client, admin_api_client, sale):
         sale.refresh_from_db()
 
 
-def test_validate_voucher(voucher, admin_api_client):
+def test_validate_voucher(
+        voucher, staff_api_client, permission_manage_discounts):
     query = """
     mutation  voucherUpdate(
         $id: ID!, $type: VoucherTypeEnum) {
@@ -312,11 +277,12 @@ def test_validate_voucher(voucher, admin_api_client):
         (VoucherTypeEnum.CATEGORY, 'categories'),
         (VoucherTypeEnum.PRODUCT, 'products'),
         (VoucherTypeEnum.COLLECTION, 'collections'))
+    staff_api_client.user.user_permissions.add(permission_manage_discounts)
     for voucher_type, field_name in fields:
         variables = {
             'type': voucher_type.name,
             'id': graphene.Node.to_global_id('Voucher', voucher.id)}
-        response = admin_api_client.post_graphql(query, variables)
+        response = staff_api_client.post_graphql(query, variables)
         content = get_graphql_content(response)
         data = content['data']['voucherUpdate']['errors'][0]
         assert data['field'] == field_name
diff --git a/tests/api/test_fulfillment.py b/tests/api/test_fulfillment.py
--- a/tests/api/test_fulfillment.py
+++ b/tests/api/test_fulfillment.py
@@ -33,7 +33,9 @@
 """
 
 
-def test_create_fulfillment(admin_api_client, order_with_lines, admin_user):
+def test_create_fulfillment(
+        staff_api_client, order_with_lines, staff_user,
+        permission_manage_orders):
     order = order_with_lines
     query = CREATE_FULFILLMENT_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
@@ -45,7 +47,8 @@ def test_create_fulfillment(admin_api_client, order_with_lines, admin_user):
         'order': order_id,
         'lines': [{'orderLineId': order_line_id, 'quantity': 1}],
         'tracking': tracking, 'notify': True}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderFulfillmentCreate']['fulfillment']
     assert data['fulfillmentOrder'] == 1
@@ -57,10 +60,10 @@ def test_create_fulfillment(admin_api_client, order_with_lines, admin_user):
     assert event_fulfillment.type == (
         OrderEvents.FULFILLMENT_FULFILLED_ITEMS.value)
     assert event_fulfillment.parameters == {'quantity': 1}
-    assert event_fulfillment.user == admin_user
+    assert event_fulfillment.user == staff_user
 
     assert event_email_sent.type == OrderEvents.EMAIL_SENT.value
-    assert event_email_sent.user == admin_user
+    assert event_email_sent.user == staff_user
     assert event_email_sent.parameters == {
         'email': order.user_email,
         'email_type': OrderEventsEmails.FULFILLMENT.value}
@@ -72,15 +75,16 @@ def test_create_fulfillment(admin_api_client, order_with_lines, admin_user):
         (0, 'Quantity must be larger than 0.'),
         (100, 'Only 3 items remaining to fulfill.')))
 def test_create_fulfillment_not_sufficient_quantity(
-        admin_api_client, order_with_lines, admin_user, quantity,
-        error_message):
+        staff_api_client, order_with_lines, staff_user, quantity,
+        error_message, permission_manage_orders):
     query = CREATE_FULFILLMENT_QUERY
     order_line = order_with_lines.lines.first()
     order_line_id = graphene.Node.to_global_id('OrderLine', order_line.id)
     variables = {
         'order': graphene.Node.to_global_id('Order', order_with_lines.id),
         'lines': [{'orderLineId': order_line_id, 'quantity': quantity}]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderFulfillmentCreate']
     assert data['errors']
@@ -88,7 +92,8 @@ def test_create_fulfillment_not_sufficient_quantity(
     assert data['errors'][0]['message'] == error_message
 
 
-def test_fulfillment_update_tracking(admin_api_client, fulfillment):
+def test_fulfillment_update_tracking(
+        staff_api_client, fulfillment, permission_manage_orders):
     query = """
     mutation updateFulfillment($id: ID!, $tracking: String) {
             orderFulfillmentUpdateTracking(
@@ -102,14 +107,15 @@ def test_fulfillment_update_tracking(admin_api_client, fulfillment):
     fulfillment_id = graphene.Node.to_global_id('Fulfillment', fulfillment.id)
     tracking = 'stationary tracking'
     variables = {'id': fulfillment_id, 'tracking': tracking}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderFulfillmentUpdateTracking']['fulfillment']
     assert data['trackingNumber'] == tracking
 
 
 def test_cancel_fulfillment_restock_items(
-        admin_api_client, fulfillment, admin_user):
+        staff_api_client, fulfillment, staff_user, permission_manage_orders):
     query = """
     mutation cancelFulfillment($id: ID!, $restock: Boolean) {
             orderFulfillmentCancel(id: $id, input: {restock: $restock}) {
@@ -121,7 +127,8 @@ def test_cancel_fulfillment_restock_items(
     """
     fulfillment_id = graphene.Node.to_global_id('Fulfillment', fulfillment.id)
     variables = {'id': fulfillment_id, 'restock': True}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderFulfillmentCancel']['fulfillment']
     assert data['status'] == FulfillmentStatus.CANCELED.upper()
@@ -130,10 +137,11 @@ def test_cancel_fulfillment_restock_items(
         OrderEvents.FULFILLMENT_RESTOCKED_ITEMS.value)
     assert event_restocked_items.parameters == {
         'quantity': fulfillment.get_total_quantity()}
-    assert event_restocked_items.user == admin_user
+    assert event_restocked_items.user == staff_user
 
 
-def test_cancel_fulfillment(admin_api_client, fulfillment, admin_user):
+def test_cancel_fulfillment(
+        staff_api_client, fulfillment, staff_user, permission_manage_orders):
     query = """
     mutation cancelFulfillment($id: ID!, $restock: Boolean) {
             orderFulfillmentCancel(id: $id, input: {restock: $restock}) {
@@ -145,7 +153,8 @@ def test_cancel_fulfillment(admin_api_client, fulfillment, admin_user):
     """
     fulfillment_id = graphene.Node.to_global_id('Fulfillment', fulfillment.id)
     variables = {'id': fulfillment_id, 'restock': False}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderFulfillmentCancel']['fulfillment']
     assert data['status'] == FulfillmentStatus.CANCELED.upper()
@@ -154,4 +163,4 @@ def test_cancel_fulfillment(admin_api_client, fulfillment, admin_user):
         OrderEvents.FULFILLMENT_CANCELED.value)
     assert event_cancel_fulfillment.parameters == {
         'composed_id': fulfillment.composed_id}
-    assert event_cancel_fulfillment.user == admin_user
+    assert event_cancel_fulfillment.user == staff_user
diff --git a/tests/api/test_menu.py b/tests/api/test_menu.py
--- a/tests/api/test_menu.py
+++ b/tests/api/test_menu.py
@@ -137,7 +137,8 @@ def test_menu_item_query_static_url(user_api_client, menu_item):
     assert not data['page']
 
 
-def test_create_menu(admin_api_client, collection, category, page):
+def test_create_menu(
+        staff_api_client, collection, category, page, permission_manage_menus):
     query = """
     mutation mc($name: String!, $collection: ID, $category: ID, $page: ID, $url: String) {
         menuCreate(input: {
@@ -170,12 +171,14 @@ def test_create_menu(admin_api_client, collection, category, page):
     variables = {
         'name': 'test-menu', 'collection': collection_id,
         'category': category_id, 'page': page_id, 'url': url}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     assert content['data']['menuCreate']['menu']['name'] == 'test-menu'
 
 
-def test_update_menu(admin_api_client, menu):
+def test_update_menu(
+        staff_api_client, menu, permission_manage_menus):
     query = """
     mutation updatemenu($id: ID!, $name: String!) {
         menuUpdate(id: $id, input: {name: $name}) {
@@ -188,12 +191,14 @@ def test_update_menu(admin_api_client, menu):
     menu_id = graphene.Node.to_global_id('Menu', menu.pk)
     name = 'Blue oyster menu'
     variables = {'id': menu_id, 'name': name}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     assert content['data']['menuUpdate']['menu']['name'] == name
 
 
-def test_delete_menu(admin_api_client, menu):
+def test_delete_menu(
+        staff_api_client, menu, permission_manage_menus):
     query = """
         mutation deletemenu($id: ID!) {
             menuDelete(id: $id) {
@@ -205,14 +210,16 @@ def test_delete_menu(admin_api_client, menu):
         """
     menu_id = graphene.Node.to_global_id('Menu', menu.pk)
     variables = {'id': menu_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     assert content['data']['menuDelete']['menu']['name'] == menu.name
     with pytest.raises(menu._meta.model.DoesNotExist):
         menu.refresh_from_db()
 
 
-def test_create_menu_item(admin_api_client, menu):
+def test_create_menu_item(
+        staff_api_client, menu, permission_manage_menus):
     query = """
     mutation createMenuItem($menu_id: ID!, $name: String!, $url: String){
         menuItemCreate(input: {name: $name, menu: $menu_id, url: $url}) {
@@ -230,7 +237,8 @@ def test_create_menu_item(admin_api_client, menu):
     url = 'http://www.example.com'
     menu_id = graphene.Node.to_global_id('Menu', menu.pk)
     variables = {'name': name, 'url': url, 'menu_id': menu_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     data = content['data']['menuItemCreate']['menuItem']
     assert data['name'] == name
@@ -238,7 +246,8 @@ def test_create_menu_item(admin_api_client, menu):
     assert data['menu']['name'] == menu.name
 
 
-def test_update_menu_item(admin_api_client, menu, menu_item, page):
+def test_update_menu_item(
+        staff_api_client, menu, menu_item, page, permission_manage_menus):
     query = """
     mutation updateMenuItem($id: ID!, $page: ID) {
         menuItemUpdate(id: $id, input: {page: $page}) {
@@ -256,13 +265,15 @@ def test_update_menu_item(admin_api_client, menu, menu_item, page):
     menu_item_id = graphene.Node.to_global_id('MenuItem', menu_item.pk)
     page_id = graphene.Node.to_global_id('Page', page.pk)
     variables = {'id': menu_item_id, 'page': page_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     data = content['data']['menuItemUpdate']['menuItem']
     assert data['page']['id'] == page_id
 
 
-def test_delete_menu_item(admin_api_client, menu_item):
+def test_delete_menu_item(
+        staff_api_client, menu_item, permission_manage_menus):
     query = """
         mutation deleteMenuItem($id: ID!) {
             menuItemDelete(id: $id) {
@@ -274,7 +285,8 @@ def test_delete_menu_item(admin_api_client, menu_item):
         """
     menu_item_id = graphene.Node.to_global_id('MenuItem', menu_item.pk)
     variables = {'id': menu_item_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     data = content['data']['menuItemDelete']['menuItem']
     assert data['name'] == menu_item.name
@@ -282,7 +294,8 @@ def test_delete_menu_item(admin_api_client, menu_item):
         menu_item.refresh_from_db()
 
 
-def test_add_more_than_one_item(admin_api_client, menu, menu_item, page):
+def test_add_more_than_one_item(
+        staff_api_client, menu, menu_item, page, permission_manage_menus):
     query = """
     mutation updateMenuItem($id: ID!, $page: ID, $url: String) {
         menuItemUpdate(id: $id,
@@ -301,7 +314,8 @@ def test_add_more_than_one_item(admin_api_client, menu, menu_item, page):
     menu_item_id = graphene.Node.to_global_id('MenuItem', menu_item.pk)
     page_id = graphene.Node.to_global_id('Page', page.pk)
     variables = {'id': menu_item_id, 'page': page_id, 'url': url}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_menus])
     content = get_graphql_content(response)
     data = content['data']['menuItemUpdate']['errors'][0]
     assert data['field'] == 'items'
diff --git a/tests/api/test_order.py b/tests/api/test_order.py
--- a/tests/api/test_order.py
+++ b/tests/api/test_order.py
@@ -21,7 +21,8 @@
 from .utils import assert_no_permission
 
 
-def test_orderline_query(admin_api_client, fulfilled_order):
+def test_orderline_query(
+        staff_api_client, permission_manage_orders, fulfilled_order):
     order = fulfilled_order
     query = """
         query OrdersQuery {
@@ -43,8 +44,8 @@ def test_orderline_query(admin_api_client, fulfilled_order):
     line = order.lines.first()
     line.variant = None
     line.save()
-
-    response = admin_api_client.post_graphql(query)
+    staff_api_client.user.user_permissions.add(permission_manage_orders)
+    response = staff_api_client.post_graphql(query)
     content = get_graphql_content(response)
     order_data = content['data']['orders']['edges'][0]['node']
     lines_data = order_data['lines']['edges']
@@ -54,7 +55,9 @@ def test_orderline_query(admin_api_client, fulfilled_order):
     assert '/static/images/placeholder540x540.png' in thumbnails
 
 
-def test_order_query(admin_api_client, fulfilled_order, shipping_zone):
+def test_order_query(
+        staff_api_client, permission_manage_orders, fulfilled_order,
+        shipping_zone):
     order = fulfilled_order
     query = """
     query OrdersQuery {
@@ -105,7 +108,8 @@ def test_order_query(admin_api_client, fulfilled_order, shipping_zone):
         }
     }
     """
-    response = admin_api_client.post_graphql(query)
+    staff_api_client.user.user_permissions.add(permission_manage_orders)
+    response = staff_api_client.post_graphql(query)
     content = get_graphql_content(response)
     order_data = content['data']['orders']['edges'][0]['node']
     assert order_data['number'] == str(order.pk)
@@ -138,7 +142,9 @@ def test_order_query(admin_api_client, fulfilled_order, shipping_zone):
     assert expected_method.type.upper() == method['type']
 
 
-def test_order_events_query(admin_api_client, fulfilled_order, admin_user):
+def test_order_events_query(
+        staff_api_client, permission_manage_orders, fulfilled_order,
+        staff_user):
     query = """
         query OrdersQuery {
             orders(first: 1) {
@@ -164,14 +170,15 @@ def test_order_events_query(admin_api_client, fulfilled_order, admin_user):
     """
     event = fulfilled_order.events.create(
         type=OrderEvents.OTHER.value,
-        user=admin_user,
+        user=staff_user,
         parameters={
             'message': 'Example note',
             'email_type': OrderEventsEmails.PAYMENT.value,
             'amount': '80.00',
             'quantity': '10',
             'composed_id': '10-10'})
-    response = admin_api_client.post_graphql(query)
+    staff_api_client.user.user_permissions.add(permission_manage_orders)
+    response = staff_api_client.post_graphql(query)
     content = get_graphql_content(response)
     data = content['data']['orders']['edges'][0]['node']['events'][0]
     assert data['message'] == event.parameters['message']
@@ -179,7 +186,7 @@ def test_order_events_query(admin_api_client, fulfilled_order, admin_user):
     assert data['emailType'] == OrderEventsEmailsEnum.PAYMENT.name
     assert data['quantity'] == int(event.parameters['quantity'])
     assert data['composedId'] == event.parameters['composed_id']
-    assert data['user']['email'] == admin_user.email
+    assert data['user']['email'] == staff_user.email
     assert data['type'] == OrderEvents.OTHER.value.upper()
     assert data['date'] == event.date.isoformat()
 
@@ -211,8 +218,8 @@ def test_non_staff_user_can_only_see_his_order(user_api_client, order):
 
 
 def test_draft_order_create(
-        admin_api_client, customer_user, product_without_shipping,
-        shipping_method, variant, voucher):
+        staff_api_client, permission_manage_orders, customer_user,
+        product_without_shipping, shipping_method, variant, voucher):
     variant_0 = variant
     query = """
     mutation draftCreate(
@@ -259,16 +266,19 @@ def test_draft_order_create(
     variant_list = [
         {'variantId': variant_0_id, 'quantity': 2},
         {'variantId': variant_1_id, 'quantity': 1}]
-    shipping_address = {
-        'firstName': 'John', 'country': 'PL'}
+    shipping_address = {'firstName': 'John', 'country': 'PL'}
     shipping_id = graphene.Node.to_global_id(
         'ShippingMethod', shipping_method.id)
     voucher_id = graphene.Node.to_global_id('Voucher', voucher.id)
     variables = {
-        'user': user_id, 'discount': discount,
-        'lines': variant_list, 'shippingAddress': shipping_address,
-        'shippingMethod': shipping_id, 'voucher': voucher_id}
-    response = admin_api_client.post_graphql(query, variables)
+        'user': user_id,
+        'discount': discount,
+        'lines': variant_list,
+        'shippingAddress': shipping_address,
+        'shippingMethod': shipping_id,
+        'voucher': voucher_id}
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderCreate']['order']
     assert data['status'] == OrderStatus.DRAFT.upper()
@@ -282,7 +292,8 @@ def test_draft_order_create(
         **{'first_name': 'John', 'country': 'PL'})
 
 
-def test_draft_order_update(admin_api_client, order_with_lines):
+def test_draft_order_update(
+        staff_api_client, permission_manage_orders, order_with_lines):
     order = order_with_lines
     query = """
         mutation draftUpdate($id: ID!, $email: String) {
@@ -300,13 +311,15 @@ def test_draft_order_update(admin_api_client, order_with_lines):
     email = 'not_default@example.com'
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id, 'email': email}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderUpdate']['order']
     assert data['userEmail'] == email
 
 
-def test_draft_order_delete(admin_api_client, order_with_lines):
+def test_draft_order_delete(
+        staff_api_client, permission_manage_orders, order_with_lines):
     order = order_with_lines
     query = """
         mutation draftDelete($id: ID!) {
@@ -319,7 +332,8 @@ def test_draft_order_delete(admin_api_client, order_with_lines):
         """
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     with pytest.raises(order._meta.model.DoesNotExist):
         order.refresh_from_db()
 
@@ -345,7 +359,8 @@ def test_check_for_draft_order_errors_no_order_lines(order):
     assert errors[0].message == 'Could not create order without any products.'
 
 
-def test_draft_order_complete(admin_api_client, admin_user, draft_order):
+def test_draft_order_complete(
+        staff_api_client, permission_manage_orders, staff_user, draft_order):
     order = draft_order
     query = """
         mutation draftComplete($id: ID!) {
@@ -364,18 +379,19 @@ def test_draft_order_complete(admin_api_client, admin_user, draft_order):
 
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderComplete']['order']
     order.refresh_from_db()
     assert data['status'] == order.status.upper()
     missing_stock_event, draft_placed_event = OrderEvent.objects.all()
 
-    assert missing_stock_event.user == admin_user
+    assert missing_stock_event.user == staff_user
     assert missing_stock_event.type == OrderEvents.OVERSOLD_ITEMS.value
     assert missing_stock_event.parameters == {'oversold_items': [str(line_2)]}
 
-    assert draft_placed_event.user == admin_user
+    assert draft_placed_event.user == staff_user
     assert draft_placed_event.type == OrderEvents.PLACED_FROM_DRAFT.value
     assert draft_placed_event.parameters == {}
 
@@ -439,7 +455,7 @@ def test_draft_order_line_create(
 
 
 def test_require_draft_order_when_creating_lines(
-        order_with_lines, admin_api_client):
+        order_with_lines, staff_api_client, permission_manage_orders):
     query = DRAFT_ORDER_LINE_CREATE_MUTATION
     order = order_with_lines
     line = order.lines.first()
@@ -447,7 +463,8 @@ def test_require_draft_order_when_creating_lines(
     order_id = graphene.Node.to_global_id('Order', order.id)
     variant_id = graphene.Node.to_global_id('ProductVariant', variant.id)
     variables = {'orderId': order_id, 'variantId': variant_id, 'quantity': 1}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderLineCreate']
     assert data['errors']
@@ -506,13 +523,14 @@ def test_draft_order_line_update(
 
 
 def test_require_draft_order_when_updating_lines(
-        order_with_lines, admin_api_client):
+        order_with_lines, staff_api_client, permission_manage_orders):
     query = DRAFT_ORDER_LINE_UPDATE_MUTATION
     order = order_with_lines
     line = order.lines.first()
     line_id = graphene.Node.to_global_id('OrderLine', line.id)
     variables = {'lineId': line_id, 'quantity': 1}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderLineUpdate']
     assert data['errors']
@@ -544,13 +562,8 @@ def test_draft_order_line_remove(
     line_id = graphene.Node.to_global_id('OrderLine', line.id)
     variables = {'id': line_id}
 
-    # mutation should fail without proper permissions
-    response = staff_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
-
-    # assign permissions
-    staff_api_client.user.user_permissions.add(permission_manage_orders)
-    response = staff_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderLineDelete']
     assert data['orderLine']['id'] == line_id
@@ -558,19 +571,21 @@ def test_draft_order_line_remove(
 
 
 def test_require_draft_order_when_removing_lines(
-        admin_api_client, order_with_lines, permission_manage_orders):
+        staff_api_client, order_with_lines, permission_manage_orders):
     query = DRAFT_ORDER_LINE_DELETE_MUTATION
     order = order_with_lines
     line = order.lines.first()
     line_id = graphene.Node.to_global_id('OrderLine', line.id)
     variables = {'id': line_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['draftOrderLineDelete']
     assert data['errors']
 
 
-def test_order_update(admin_api_client, order_with_lines):
+def test_order_update(
+        staff_api_client, permission_manage_orders, order_with_lines):
     order = order_with_lines
     query = """
         mutation orderUpdate(
@@ -600,9 +615,13 @@ def test_order_update(admin_api_client, order_with_lines):
     assert not order.billing_address.last_name == last_name
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {
-        'id': order_id, 'email': email, 'first_name': first_name,
-        'last_name': last_name, 'country_code': 'PL'}
-    response = admin_api_client.post_graphql(query, variables)
+        'id': order_id,
+        'email': email,
+        'first_name': first_name,
+        'last_name': last_name,
+        'country_code': 'PL'}
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdate']['order']
     assert data['userEmail'] == email
@@ -612,7 +631,9 @@ def test_order_update(admin_api_client, order_with_lines):
     assert order.billing_address.last_name == last_name
 
 
-def test_order_add_note(admin_api_client, order_with_lines, admin_user):
+def test_order_add_note(
+        staff_api_client, permission_manage_orders, order_with_lines,
+        staff_user):
     order = order_with_lines
     query = """
         mutation addNote($id: ID!, $message: String) {
@@ -637,17 +658,18 @@ def test_order_add_note(admin_api_client, order_with_lines, admin_user):
     order_id = graphene.Node.to_global_id('Order', order.id)
     message = 'nuclear note'
     variables = {'id': order_id, 'message': message}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderAddNote']
 
     assert data['order']['id'] == order_id
-    assert data['event']['user']['email'] == admin_user.email
+    assert data['event']['user']['email'] == staff_user.email
     assert data['event']['message'] == message
 
     event = order.events.get()
     assert event.type == OrderEvents.NOTE_ADDED.value
-    assert event.user == admin_user
+    assert event.user == staff_user
     assert event.parameters == {'message': message}
 
 
@@ -662,14 +684,16 @@ def test_order_add_note(admin_api_client, order_with_lines, admin_user):
 """
 
 
-def test_order_cancel_and_restock(admin_api_client, order_with_lines):
+def test_order_cancel_and_restock(
+        staff_api_client, permission_manage_orders, order_with_lines):
     order = order_with_lines
     query = CANCEL_ORDER_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
     restock = True
     quantity = order.get_total_quantity()
     variables = {'id': order_id, 'restock': restock}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderCancel']['order']
     order.refresh_from_db()
@@ -679,13 +703,15 @@ def test_order_cancel_and_restock(admin_api_client, order_with_lines):
     assert data['status'] == order.status.upper()
 
 
-def test_order_cancel(admin_api_client, order_with_lines):
+def test_order_cancel(
+        staff_api_client, permission_manage_orders, order_with_lines):
     order = order_with_lines
     query = CANCEL_ORDER_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
     restock = False
     variables = {'id': order_id, 'restock': restock}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderCancel']['order']
     order.refresh_from_db()
@@ -694,7 +720,8 @@ def test_order_cancel(admin_api_client, order_with_lines):
     assert data['status'] == order.status.upper()
 
 
-def test_order_capture(admin_api_client, payment_preauth, admin_user):
+def test_order_capture(
+        staff_api_client, permission_manage_orders, payment_preauth, staff_user):
     order = payment_preauth.order
     query = """
         mutation captureOrder($id: ID!, $amount: Decimal!) {
@@ -712,7 +739,8 @@ def test_order_capture(admin_api_client, payment_preauth, admin_user):
     order_id = graphene.Node.to_global_id('Order', order.id)
     amount = float(payment_preauth.get_total().gross.amount)
     variables = {'id': order_id, 'amount': amount}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderCapture']['order']
     order.refresh_from_db()
@@ -730,12 +758,12 @@ def test_order_capture(admin_api_client, payment_preauth, admin_user):
         'email': order.user_email,
         'email_type': OrderEventsEmails.PAYMENT.value}
     assert event_captured.type == OrderEvents.PAYMENT_CAPTURED.value
-    assert event_captured.user == admin_user
+    assert event_captured.user == staff_user
     assert event_captured.parameters == {'amount': str(amount)}
 
 
 def test_paid_order_mark_as_paid(
-        admin_api_client, payment_preauth):
+        staff_api_client, permission_manage_orders, payment_preauth):
     order = payment_preauth.order
     query = """
             mutation markPaid($id: ID!) {
@@ -752,7 +780,8 @@ def test_paid_order_mark_as_paid(
         """
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     errors = content['data']['orderMarkAsPaid']['errors']
     msg = 'Orders with payments can not be manually marked as paid.'
@@ -761,7 +790,8 @@ def test_paid_order_mark_as_paid(
 
 
 def test_order_mark_as_paid(
-        admin_api_client, order_with_lines, admin_user):
+        staff_api_client, permission_manage_orders, order_with_lines,
+        staff_user):
     order = order_with_lines
     query = """
             mutation markPaid($id: ID!) {
@@ -779,7 +809,8 @@ def test_order_mark_as_paid(
     assert not order.is_fully_paid()
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderMarkAsPaid']['order']
     order.refresh_from_db()
@@ -787,10 +818,11 @@ def test_order_mark_as_paid(
 
     event_order_paid = order.events.first()
     assert event_order_paid.type == OrderEvents.ORDER_MARKED_AS_PAID.value
-    assert event_order_paid.user == admin_user
+    assert event_order_paid.user == staff_user
 
 
-def test_order_release(admin_api_client, payment_preauth, admin_user):
+def test_order_release(
+        staff_api_client, permission_manage_orders, payment_preauth, staff_user):
     order = payment_preauth.order
     query = """
             mutation releaseOrder($id: ID!) {
@@ -803,16 +835,18 @@ def test_order_release(admin_api_client, payment_preauth, admin_user):
         """
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'id': order_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderRelease']['order']
     assert data['paymentStatus'] == PaymentStatusEnum.REFUNDED.name
     event_payment_released = order.events.last()
     assert event_payment_released.type == OrderEvents.PAYMENT_RELEASED.value
-    assert event_payment_released.user == admin_user
+    assert event_payment_released.user == staff_user
 
 
-def test_order_refund(admin_api_client, payment_confirmed):
+def test_order_refund(
+        staff_api_client, permission_manage_orders, payment_confirmed):
     order = order = payment_confirmed.order
     query = """
         mutation refundOrder($id: ID!, $amount: Decimal!) {
@@ -828,7 +862,8 @@ def test_order_refund(admin_api_client, payment_confirmed):
     order_id = graphene.Node.to_global_id('Order', order.id)
     amount = float(payment_confirmed.get_total().gross.amount)
     variables = {'id': order_id, 'amount': amount}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderRefund']['order']
     order.refresh_from_db()
@@ -912,14 +947,16 @@ def test_clean_order_cancel(order):
 
 
 def test_order_update_shipping(
-        admin_api_client, order_with_lines, shipping_method, admin_user):
+        staff_api_client, permission_manage_orders, order_with_lines,
+        shipping_method, staff_user):
     order = order_with_lines
     query = ORDER_UPDATE_SHIPPING_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
     method_id = graphene.Node.to_global_id(
         'ShippingMethod', shipping_method.id)
     variables = {'order': order_id, 'shippingMethod': method_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdateShipping']
     assert data['order']['id'] == order_id
@@ -933,7 +970,8 @@ def test_order_update_shipping(
 
 
 def test_order_update_shipping_clear_shipping_method(
-        admin_api_client, order, admin_user, shipping_method):
+        staff_api_client, permission_manage_orders, order, staff_user,
+        shipping_method):
     order.shipping_method = shipping_method
     order.shipping_price = shipping_method.get_total()
     order.shipping_method_name = 'Example shipping'
@@ -942,7 +980,8 @@ def test_order_update_shipping_clear_shipping_method(
     query = ORDER_UPDATE_SHIPPING_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'order': order_id, 'shippingMethod': None}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdateShipping']
     assert data['order']['id'] == order_id
@@ -954,12 +993,14 @@ def test_order_update_shipping_clear_shipping_method(
 
 
 def test_order_update_shipping_shipping_required(
-        admin_api_client, order_with_lines, admin_user):
+        staff_api_client, permission_manage_orders, order_with_lines,
+        staff_user):
     order = order_with_lines
     query = ORDER_UPDATE_SHIPPING_QUERY
     order_id = graphene.Node.to_global_id('Order', order.id)
     variables = {'order': order_id, 'shippingMethod': None}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdateShipping']
     assert data['errors'][0]['field'] == 'shippingMethod'
@@ -968,7 +1009,8 @@ def test_order_update_shipping_shipping_required(
 
 
 def test_order_update_shipping_no_shipping_address(
-        admin_api_client, order_with_lines, shipping_method, admin_user):
+        staff_api_client, permission_manage_orders, order_with_lines,
+        shipping_method, staff_user):
     order = order_with_lines
     order.shipping_address = None
     order.save()
@@ -977,7 +1019,8 @@ def test_order_update_shipping_no_shipping_address(
     method_id = graphene.Node.to_global_id(
         'ShippingMethod', shipping_method.id)
     variables = {'order': order_id, 'shippingMethod': method_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdateShipping']
     assert data['errors'][0]['field'] == 'order'
@@ -987,7 +1030,8 @@ def test_order_update_shipping_no_shipping_address(
 
 
 def test_order_update_shipping_incorrect_shipping_method(
-        admin_api_client, order_with_lines, shipping_method, admin_user):
+        staff_api_client, permission_manage_orders, order_with_lines,
+        shipping_method, staff_user):
     order = order_with_lines
     zone = shipping_method.shipping_zone
     zone.countries = ['DE']
@@ -998,7 +1042,8 @@ def test_order_update_shipping_incorrect_shipping_method(
     method_id = graphene.Node.to_global_id(
         'ShippingMethod', shipping_method.id)
     variables = {'order': order_id, 'shippingMethod': method_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_orders])
     content = get_graphql_content(response)
     data = content['data']['orderUpdateShipping']
     assert data['errors'][0]['field'] == 'shippingMethod'
diff --git a/tests/api/test_page.py b/tests/api/test_page.py
--- a/tests/api/test_page.py
+++ b/tests/api/test_page.py
@@ -23,7 +23,7 @@ def test_page_query(user_api_client, page):
     assert page_data['slug'] == page.slug
 
 
-def test_page_create_mutation(admin_api_client):
+def test_page_create_mutation(staff_api_client, permission_manage_pages):
     query = """
         mutation CreatePage(
                 $slug: String!, $title: String!, $content: String!,
@@ -55,7 +55,8 @@ def test_page_create_mutation(admin_api_client):
     variables = {
         'title': page_title, 'content': page_content,
         'isVisible': page_isVisible, 'slug': page_slug}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_pages])
     content = get_graphql_content(response)
     data = content['data']['pageCreate']
     assert data['errors'] == []
@@ -65,7 +66,7 @@ def test_page_create_mutation(admin_api_client):
     assert data['page']['isVisible'] == page_isVisible
 
 
-def test_page_delete_mutation(admin_api_client, page):
+def test_page_delete_mutation(staff_api_client, page, permission_manage_pages):
     query = """
         mutation DeletePage($id: ID!) {
             pageDelete(id: $id) {
@@ -81,7 +82,8 @@ def test_page_delete_mutation(admin_api_client, page):
             }
     """
     variables = {'id': graphene.Node.to_global_id('Page', page.id)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_pages])
     content = get_graphql_content(response)
     data = content['data']['pageDelete']
     assert data['page']['title'] == page.title
diff --git a/tests/api/test_product.py b/tests/api/test_product.py
--- a/tests/api/test_product.py
+++ b/tests/api/test_product.py
@@ -56,7 +56,7 @@ def test_fetch_unavailable_products(user_api_client, product):
     assert not content['data']['products']['edges']
 
 
-def test_product_query(admin_api_client, product):
+def test_product_query(staff_api_client, product, permission_manage_products):
     category = Category.objects.first()
     product = category.products.first()
     query = """
@@ -120,7 +120,8 @@ def test_product_query(admin_api_client, product):
         }
     }
     """ % {'category_id': graphene.Node.to_global_id('Category', category.id)}
-    response = admin_api_client.post_graphql(query)
+    staff_api_client.user.user_permissions.add(permission_manage_products)
+    response = staff_api_client.post_graphql(query)
     content = get_graphql_content(response)
     assert content['data']['category'] is not None
     product_edges_data = content['data']['category']['products']['edges']
@@ -159,7 +160,8 @@ def test_query_product_image_by_id(user_api_client, product_with_image):
     content = get_graphql_content(response)
 
 
-def test_product_with_collections(admin_api_client, product, collection):
+def test_product_with_collections(
+        staff_api_client, product, collection, permission_manage_products):
     query = """
         query getProduct($productID: ID!) {
             product(id: $productID) {
@@ -178,7 +180,8 @@ def test_product_with_collections(admin_api_client, product, collection):
     product_id = graphene.Node.to_global_id('Product', product.id)
 
     variables = {'productID': product_id}
-    response = admin_api_client.post_graphql(query, variables)
+    staff_api_client.user.user_permissions.add(permission_manage_products)
+    response = staff_api_client.post_graphql(query, variables)
     content = get_graphql_content(response)
     data = content['data']['product']
     assert data['collections']['edges'][0]['node']['name'] == collection.name
@@ -291,7 +294,8 @@ def test_sort_products(user_api_client, product):
 
 
 def test_create_product(
-        admin_api_client, product_type, category, size_attribute):
+        staff_api_client, product_type, category, size_attribute,
+        permission_manage_products):
     query = """
         mutation createProduct(
             $productTypeId: ID!,
@@ -382,7 +386,8 @@ def test_create_product(
             {'slug': color_attr_slug, 'value': color_value_slug},
             {'slug': size_attr_slug, 'value': non_existent_attr_value}]}
 
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productCreate']
     assert data['errors'] == []
@@ -401,7 +406,8 @@ def test_create_product(
 
 
 def test_update_product(
-        admin_api_client, category, non_default_category, product):
+        staff_api_client, category, non_default_category, product,
+        permission_manage_products):
     query = """
         mutation updateProduct(
             $productId: ID!,
@@ -476,7 +482,8 @@ def test_update_product(
         'taxRate': product_taxRate,
         'price': product_price}
 
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productUpdate']
     assert data['errors'] == []
@@ -488,7 +495,7 @@ def test_update_product(
     assert not data['product']['category']['name'] == category.name
 
 
-def test_delete_product(admin_api_client, product):
+def test_delete_product(staff_api_client, product, permission_manage_products):
     query = """
         mutation DeleteProduct($id: ID!) {
             productDelete(id: $id) {
@@ -505,7 +512,8 @@ def test_delete_product(admin_api_client, product):
     """
     node_id = graphene.Node.to_global_id('Product', product.id)
     variables = {'id': node_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productDelete']
     assert data['product']['name'] == product.name
@@ -543,7 +551,8 @@ def test_product_type(user_api_client, product_type):
 
 
 def test_product_type_query(
-        user_api_client, admin_api_client, product_type, product):
+        user_api_client, staff_api_client, product_type, product,
+        permission_manage_products):
     query = """
             query getProductType($id: ID!) {
                 productType(id: $id) {
@@ -571,14 +580,16 @@ def test_product_type_query(
     data = content['data']
     assert data['productType']['products']['totalCount'] == no_products - 1
 
-    response = admin_api_client.post_graphql(query, variables)
+    staff_api_client.user.user_permissions.add(permission_manage_products)
+    response = staff_api_client.post_graphql(query, variables)
     content = get_graphql_content(response)
     data = content['data']
     assert data['productType']['products']['totalCount'] == no_products
     assert data['productType']['taxRate'] == product_type.tax_rate.upper()
 
 
-def test_product_type_create_mutation(admin_api_client, product_type):
+def test_product_type_create_mutation(
+        staff_api_client, product_type, permission_manage_products):
     query = """
     mutation createProductType(
         $name: String!,
@@ -635,7 +646,8 @@ def test_product_type_create_mutation(admin_api_client, product_type):
         'productAttributes': product_attributes_ids,
         'variantAttributes': variant_attributes_ids}
     initial_count = ProductType.objects.count()
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert ProductType.objects.count() == initial_count + 1
     data = content['data']['productTypeCreate']['productType']
@@ -659,7 +671,8 @@ def test_product_type_create_mutation(admin_api_client, product_type):
     assert new_instance.tax_rate == 'standard'
 
 
-def test_product_type_update_mutation(admin_api_client, product_type):
+def test_product_type_update_mutation(
+        staff_api_client, product_type, permission_manage_products):
     query = """
     mutation updateProductType(
         $id: ID!,
@@ -709,7 +722,8 @@ def test_product_type_update_mutation(admin_api_client, product_type):
         'hasVariants': has_variants,
         'isShippingRequired': require_shipping,
         'productAttributes': product_attributes_ids}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productTypeUpdate']['productType']
     assert data['name'] == product_type_name
@@ -720,7 +734,8 @@ def test_product_type_update_mutation(admin_api_client, product_type):
         variant_attributes.count())
 
 
-def test_product_type_delete_mutation(admin_api_client, product_type):
+def test_product_type_delete_mutation(
+        staff_api_client, product_type, permission_manage_products):
     query = """
         mutation deleteProductType($id: ID!) {
             productTypeDelete(id: $id) {
@@ -732,7 +747,8 @@ def test_product_type_delete_mutation(admin_api_client, product_type):
     """
     variables = {
         'id': graphene.Node.to_global_id('ProductType', product_type.id)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productTypeDelete']
     assert data['productType']['name'] == product_type.name
@@ -740,7 +756,8 @@ def test_product_type_delete_mutation(admin_api_client, product_type):
         product_type.refresh_from_db()
 
 
-def test_product_image_create_mutation(admin_api_client, product):
+def test_product_image_create_mutation(
+        staff_api_client, product, permission_manage_products):
     query = """
     mutation createProductImage($image: Upload!, $product: ID!) {
         productImageCreate(input: {image: $image, product: $product}) {
@@ -755,13 +772,15 @@ def test_product_image_create_mutation(admin_api_client, product):
         'product': graphene.Node.to_global_id('Product', product.id),
         'image': image_name}
     body = get_multipart_request_body(query, variables, image_file, image_name)
-    response = admin_api_client.post_multipart(body)
+    response = staff_api_client.post_multipart(
+        body, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     product.refresh_from_db()
     assert product.images.first().image.file
 
 
-def test_invalid_product_image_create_mutation(admin_api_client, product):
+def test_invalid_product_image_create_mutation(
+        staff_api_client, product, permission_manage_products):
     query = """
     mutation createProductImage($image: Upload!, $product: ID!) {
         productImageCreate(input: {image: $image, product: $product}) {
@@ -782,7 +801,8 @@ def test_invalid_product_image_create_mutation(admin_api_client, product):
         'product': graphene.Node.to_global_id('Product', product.id),
         'image': image_name}
     body = get_multipart_request_body(query, variables, image_file, image_name)
-    response = admin_api_client.post_multipart(body)
+    response = staff_api_client.post_multipart(
+        body, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert content['data']['productImageCreate']['errors'] == [{
         'field': 'image',
@@ -791,7 +811,8 @@ def test_invalid_product_image_create_mutation(admin_api_client, product):
     assert product.images.count() == 0
 
 
-def test_product_image_update_mutation(admin_api_client, product_with_image):
+def test_product_image_update_mutation(
+        staff_api_client, product_with_image, permission_manage_products):
     query = """
     mutation updateProductImage($imageId: ID!, $alt: String) {
         productImageUpdate(id: $imageId, input: {alt: $alt}) {
@@ -806,12 +827,14 @@ def test_product_image_update_mutation(admin_api_client, product_with_image):
     variables = {
         'alt': alt,
         'imageId': graphene.Node.to_global_id('ProductImage', image_obj.id)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert content['data']['productImageUpdate']['image']['alt'] == alt
 
 
-def test_product_image_delete(admin_api_client, product_with_image):
+def test_product_image_delete(
+        staff_api_client, product_with_image, permission_manage_products):
     product = product_with_image
     query = """
             mutation deleteProductImage($id: ID!) {
@@ -826,7 +849,8 @@ def test_product_image_delete(admin_api_client, product_with_image):
     image_obj = product.images.first()
     node_id = graphene.Node.to_global_id('ProductImage', image_obj.id)
     variables = {'id': node_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productImageDelete']
     assert data['image']['url'] == image_obj.image.url
@@ -835,7 +859,8 @@ def test_product_image_delete(admin_api_client, product_with_image):
     assert node_id == data['image']['id']
 
 
-def test_reorder_images(admin_api_client, product_with_images):
+def test_reorder_images(
+        staff_api_client, product_with_images, permission_manage_products):
     query = """
     mutation reorderImages($product_id: ID!, $images_ids: [ID]!) {
         productImageReorder(productId: $product_id, imagesIds: $images_ids) {
@@ -855,7 +880,8 @@ def test_reorder_images(admin_api_client, product_with_images):
 
     variables = {
         'product_id': product_id, 'images_ids': [image_1_id, image_0_id]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
 
     # Check if order has been changed
@@ -906,7 +932,8 @@ def test_collections_query(
     assert len(edges) == 2
 
 
-def test_create_collection(admin_api_client, product_list):
+def test_create_collection(
+        staff_api_client, product_list, permission_manage_products):
     query = """
         mutation createCollection(
             $name: String!, $slug: String!, $products: [ID], $backgroundImage: Upload!, $isPublished: Boolean!) {
@@ -931,7 +958,8 @@ def test_create_collection(admin_api_client, product_list):
         'name': name, 'slug': slug, 'products': product_ids,
         'backgroundImage': image_name, 'isPublished': True}
     body = get_multipart_request_body(query, variables, image_file, image_name)
-    response = admin_api_client.post_multipart(body)
+    response = staff_api_client.post_multipart(
+        body, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['collectionCreate']['collection']
     assert data['name'] == name
@@ -941,7 +969,8 @@ def test_create_collection(admin_api_client, product_list):
     assert collection.background_image.file
 
 
-def test_update_collection(admin_api_client, collection):
+def test_update_collection(
+        staff_api_client, collection, permission_manage_products):
     query = """
         mutation updateCollection(
             $name: String!, $slug: String!, $id: ID!, $isPublished: Boolean!) {
@@ -959,14 +988,16 @@ def test_update_collection(admin_api_client, collection):
     slug = 'new-slug'
     variables = {
         'name': name, 'slug': slug, 'id': collection_id, 'isPublished': True}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['collectionUpdate']['collection']
     assert data['name'] == name
     assert data['slug'] == slug
 
 
-def test_delete_collection(admin_api_client, collection):
+def test_delete_collection(
+        staff_api_client, collection, permission_manage_products):
     query = """
         mutation deleteCollection($id: ID!) {
             collectionDelete(id: $id) {
@@ -978,7 +1009,8 @@ def test_delete_collection(admin_api_client, collection):
     """
     collection_id = to_global_id('Collection', collection.id)
     variables = {'id': collection_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['collectionDelete']['collection']
     assert data['name'] == collection.name
@@ -987,7 +1019,8 @@ def test_delete_collection(admin_api_client, collection):
 
 
 def test_add_products_to_collection(
-        admin_api_client, collection, product_list):
+        staff_api_client, collection, product_list,
+        permission_manage_products):
     query = """
         mutation collectionAddProducts(
             $id: ID!, $products: [ID]!) {
@@ -1005,7 +1038,8 @@ def test_add_products_to_collection(
         to_global_id('Product', product.pk) for product in product_list]
     no_products_before = collection.products.count()
     variables = {'id': collection_id, 'products': product_ids}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['collectionAddProducts']['collection']
     assert data[
@@ -1013,7 +1047,8 @@ def test_add_products_to_collection(
 
 
 def test_remove_products_from_collection(
-        admin_api_client, collection, product_list):
+        staff_api_client, collection, product_list,
+        permission_manage_products):
     query = """
         mutation collectionRemoveProducts(
             $id: ID!, $products: [ID]!) {
@@ -1032,16 +1067,15 @@ def test_remove_products_from_collection(
         to_global_id('Product', product.pk) for product in product_list]
     no_products_before = collection.products.count()
     variables = {'id': collection_id, 'products': product_ids}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['collectionRemoveProducts']['collection']
     assert data[
         'products']['totalCount'] == no_products_before - len(product_ids)
 
 
-def test_assign_variant_image(
-        admin_api_client, user_api_client, product_with_image):
-    query = """
+ASSIGN_VARIANT_QUERY = """
     mutation assignVariantImageMutation($variantId: ID!, $imageId: ID!) {
         variantImageAssign(variantId: $variantId, imageId: $imageId) {
             errors {
@@ -1053,19 +1087,31 @@ def test_assign_variant_image(
             }
         }
     }
-    """
+"""
+
+
+def test_assign_variant_image(
+        staff_api_client, user_api_client, product_with_image,
+        permission_manage_products):
+    query = ASSIGN_VARIANT_QUERY
     variant = product_with_image.variants.first()
     image = product_with_image.images.first()
 
     variables = {
         'variantId': to_global_id('ProductVariant', variant.pk),
         'imageId': to_global_id('ProductImage', image.pk)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     variant.refresh_from_db()
     assert variant.images.first() == image
 
-    # check that assigning an image from a different product is not allowed
+
+def test_assign_variant_image_from_different_product(
+        staff_api_client, user_api_client, product_with_image,
+        permission_manage_products):
+    query = ASSIGN_VARIANT_QUERY
+    variant = product_with_image.variants.first()
     product_with_image.pk = None
     product_with_image.save()
 
@@ -1073,7 +1119,8 @@ def test_assign_variant_image(
     variables = {
         'variantId': to_global_id('ProductVariant', variant.pk),
         'imageId': to_global_id('ProductImage', image_2.pk)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert content['data']['variantImageAssign']['errors'][0]['field'] == 'imageId'
 
@@ -1082,13 +1129,7 @@ def test_assign_variant_image(
     assert_no_permission(response)
 
 
-def test_unassign_variant_image(
-        admin_api_client, user_api_client, product_with_image):
-    image = product_with_image.images.first()
-    variant = product_with_image.variants.first()
-    variant.variant_images.create(image=image)
-
-    query = """
+UNASSIGN_VARIANT_IMAGE_QUERY = """
     mutation unassignVariantImageMutation($variantId: ID!, $imageId: ID!) {
         variantImageUnassign(variantId: $variantId, imageId: $imageId) {
             errors {
@@ -1100,32 +1141,44 @@ def test_unassign_variant_image(
             }
         }
     }
-    """
+"""
+
+
+def test_unassign_variant_image(
+        staff_api_client, product_with_image, permission_manage_products):
+    query = UNASSIGN_VARIANT_IMAGE_QUERY
+
+    image = product_with_image.images.first()
+    variant = product_with_image.variants.first()
+    variant.variant_images.create(image=image)
 
     variables = {
         'variantId': to_global_id('ProductVariant', variant.pk),
         'imageId': to_global_id('ProductImage', image.pk)}
-    response = admin_api_client.post_graphql(query, variables)
-    content = get_graphql_content(response)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
+    get_graphql_content(response)
     variant.refresh_from_db()
     assert variant.images.count() == 0
 
-    # check that unsassigning a not assigned image throws error
+
+def test_unassign_not_assigned_variant_image(
+        staff_api_client, product_with_image, permission_manage_products):
+    query = UNASSIGN_VARIANT_IMAGE_QUERY
+    variant = product_with_image.variants.first()
     image_2 = ProductImage.objects.create(product=product_with_image)
     variables = {
         'variantId': to_global_id('ProductVariant', variant.pk),
         'imageId': to_global_id('ProductImage', image_2.pk)}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
-    assert content['data']['variantImageUnassign']['errors'][0]['field'] == 'imageId'
-
-    # check permissions
-    response = user_api_client.post_graphql(query, variables)
-    assert_no_permission(response)
+    assert content['data']['variantImageUnassign']['errors'][0]['field'] == (
+        'imageId')
 
 
 def test_product_type_update_changes_variant_name(
-        admin_api_client, product_type, product):
+        staff_api_client, product_type, product, permission_manage_products):
     query = """
     mutation updateProductType(
         $id: ID!,
@@ -1162,7 +1215,8 @@ def test_product_type_update_changes_variant_name(
         'hasVariants': has_variants,
         'isShippingRequired': require_shipping,
         'variantAttributes': variant_attributes_ids}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     product.refresh_from_db()
     variant = product.variants.first()
diff --git a/tests/api/test_shipping_method.py b/tests/api/test_shipping_method.py
--- a/tests/api/test_shipping_method.py
+++ b/tests/api/test_shipping_method.py
@@ -5,66 +5,8 @@
 from tests.api.utils import get_graphql_content
 
 
-@pytest.fixture
-def weight_based_shipping_query():
-    """Dummy weight based createShippingPrice query for tests reusability."""
-    return """
-    mutation createShipipngPrice(
-        $type: ShippingMethodTypeEnum, $name: String!, $price: Decimal,
-        $shippingZone: ID!, $maximumOrderWeight: WeightScalar,
-        $minimumOrderWeight: WeightScalar) {
-        shippingPriceCreate(
-            input: {
-                name: $name, price: $price, shippingZone: $shippingZone,
-                minimumOrderWeight:$minimumOrderWeight,
-                maximumOrderWeight: $maximumOrderWeight, type: $type}) {
-            errors {
-                field
-                message
-            }
-            shippingMethod {
-                minimumOrderWeight {
-                    value
-                    unit
-                }
-                maximumOrderWeight {
-                    value
-                    unit}}}}
-    """
-
-
-@pytest.fixture
-def price_based_shipping_query():
-    """Dummy price based createShippingPrice query for tests reusability."""
-    return """
-        mutation createShipipngPrice(
-            $type: ShippingMethodTypeEnum, $name: String!, $price: Decimal,
-            $shippingZone: ID!, $minimumOrderPrice: Decimal,
-            $maximumOrderPrice: Decimal) {
-        shippingPriceCreate(input: {
-                name: $name, price: $price, shippingZone: $shippingZone,
-                minimumOrderPrice: $minimumOrderPrice,
-                maximumOrderPrice: $maximumOrderPrice, type: $type}) {
-            errors {
-                field
-                message
-            }
-            shippingMethod {
-                name
-                price {
-                    amount
-                }
-                minimumOrderPrice {
-                    amount
-                }
-                maximumOrderPrice {
-                    amount
-                }
-                type}}}
-    """
-
-
-def test_shipping_zone_query(user_api_client, shipping_zone):
+def test_shipping_zone_query(
+        staff_api_client, shipping_zone, permission_manage_shipping):
     shipping = shipping_zone
     query = """
     query ShippingQuery($id: ID!) {
@@ -88,7 +30,8 @@ def test_shipping_zone_query(user_api_client, shipping_zone):
     """
     ID = graphene.Node.to_global_id('ShippingZone', shipping.id)
     variables = {'id': ID}
-    response = user_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
 
     shipping_data = content['data']['shippingZone']
@@ -101,7 +44,8 @@ def test_shipping_zone_query(user_api_client, shipping_zone):
     assert data_price_range['stop']['amount'] == price_range.stop.amount
 
 
-def test_shipping_zones_query(user_api_client, shipping_zone):
+def test_shipping_zones_query(
+        staff_api_client, shipping_zone, permission_manage_shipping):
     query = """
     query MultipleShippings {
         shippingZones {
@@ -110,8 +54,8 @@ def test_shipping_zones_query(user_api_client, shipping_zone):
     }
     """
     num_of_shippings = shipping_zone._meta.model.objects.count()
-
-    response = user_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     assert content['data']['shippingZones']['totalCount'] == num_of_shippings
 
@@ -138,10 +82,11 @@ def test_shipping_zones_query(user_api_client, shipping_zone):
 """
 
 
-def test_create_shipping_zone(admin_api_client):
+def test_create_shipping_zone(staff_api_client, permission_manage_shipping):
     query = CREATE_SHIPPING_ZONE_QUERY
     variables = {'name': 'test shipping', 'countries': ['PL']}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneCreate']
     assert not data['errors']
@@ -151,10 +96,12 @@ def test_create_shipping_zone(admin_api_client):
     assert zone['default'] == False
 
 
-def test_create_default_shipping_zone(admin_api_client):
+def test_create_default_shipping_zone(
+        staff_api_client, permission_manage_shipping):
     query = CREATE_SHIPPING_ZONE_QUERY
     variables = {'default': True, 'name': 'test shipping', 'countries': ['PL']}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneCreate']
     assert not data['errors']
@@ -165,13 +112,14 @@ def test_create_default_shipping_zone(admin_api_client):
 
 
 def test_create_duplicated_default_shipping_zone(
-        admin_api_client, shipping_zone):
+        staff_api_client, shipping_zone, permission_manage_shipping):
     shipping_zone.default = True
     shipping_zone.save()
 
     query = CREATE_SHIPPING_ZONE_QUERY
     variables = {'default': True, 'name': 'test shipping', 'countries': ['PL']}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneCreate']
     assert data['errors']
@@ -199,12 +147,14 @@ def test_create_duplicated_default_shipping_zone(
 """
 
 
-def test_update_shipping_zone(admin_api_client, shipping_zone):
+def test_update_shipping_zone(
+        staff_api_client, shipping_zone, permission_manage_shipping):
     query = UPDATE_SHIPPING_ZONE_QUERY
     name = 'Parabolic name'
     shipping_id = graphene.Node.to_global_id('ShippingZone', shipping_zone.pk)
     variables = {'id': shipping_id, 'name': name, 'countries': []}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneUpdate']
     assert not data['errors']
@@ -213,7 +163,7 @@ def test_update_shipping_zone(admin_api_client, shipping_zone):
 
 
 def test_update_shipping_zone_default_exists(
-        admin_api_client, shipping_zone):
+        staff_api_client, shipping_zone, permission_manage_shipping):
     query = UPDATE_SHIPPING_ZONE_QUERY
     default_zone = shipping_zone
     default_zone.default = True
@@ -223,8 +173,12 @@ def test_update_shipping_zone_default_exists(
 
     shipping_id = graphene.Node.to_global_id('ShippingZone', shipping_zone.pk)
     variables = {
-        'id': shipping_id, 'name': 'Name', 'countries': [], 'default': True}
-    response = admin_api_client.post_graphql(query, variables)
+        'id': shipping_id,
+        'name': 'Name',
+        'countries': [],
+        'default': True}
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneUpdate']
     assert data['errors']
@@ -233,7 +187,8 @@ def test_update_shipping_zone_default_exists(
         'Default shipping zone already exists.')
 
 
-def test_delete_shipping_zone(admin_api_client, shipping_zone):
+def test_delete_shipping_zone(
+        staff_api_client, shipping_zone, permission_manage_shipping):
     query = """
         mutation deleteShippingZone($id: ID!) {
             shippingZoneDelete(id: $id) {
@@ -246,7 +201,8 @@ def test_delete_shipping_zone(admin_api_client, shipping_zone):
     shipping_zone_id = graphene.Node.to_global_id(
         'ShippingZone', shipping_zone.pk)
     variables = {'id': shipping_zone_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingZoneDelete']['shippingZone']
     assert data['name'] == shipping_zone.name
@@ -254,24 +210,60 @@ def test_delete_shipping_zone(admin_api_client, shipping_zone):
         shipping_zone.refresh_from_db()
 
 
+PRICE_BASED_SHIPPING_QUERY = """
+    mutation createShipipngPrice(
+        $type: ShippingMethodTypeEnum, $name: String!, $price: Decimal,
+        $shippingZone: ID!, $minimumOrderPrice: Decimal,
+        $maximumOrderPrice: Decimal) {
+    shippingPriceCreate(input: {
+            name: $name, price: $price, shippingZone: $shippingZone,
+            minimumOrderPrice: $minimumOrderPrice,
+            maximumOrderPrice: $maximumOrderPrice, type: $type}) {
+        errors {
+            field
+            message
+        }
+        shippingMethod {
+            name
+            price {
+                amount
+            }
+            minimumOrderPrice {
+                amount
+            }
+            maximumOrderPrice {
+                amount
+            }
+            type
+            }
+        }
+    }
+"""
+
+
 @pytest.mark.parametrize(
     'min_price, max_price, expected_min_price, expected_max_price',
-    (
-        (10.32, 15.43, {'amount': 10.32}, {'amount': 15.43}),
-        (10.33, None, {'amount': 10.33}, None)))
+    ((10.32, 15.43, {
+        'amount': 10.32}, {
+            'amount': 15.43}), (10.33, None, {
+                'amount': 10.33}, None)))
 def test_create_shipping_method(
-        admin_api_client, shipping_zone, min_price, max_price,
-        expected_min_price, expected_max_price, price_based_shipping_query):
-    query = price_based_shipping_query
+        staff_api_client, shipping_zone, min_price, max_price,
+        expected_min_price, expected_max_price, permission_manage_shipping):
+    query = PRICE_BASED_SHIPPING_QUERY
     name = 'DHL'
     price = 12.34
     shipping_zone_id = graphene.Node.to_global_id(
         'ShippingZone', shipping_zone.pk)
     variables = {
-        'shippingZone': shipping_zone_id, 'name': name, 'price': price,
-        'minimumOrderPrice': min_price, 'maximumOrderPrice': max_price,
+        'shippingZone': shipping_zone_id,
+        'name': name,
+        'price': price,
+        'minimumOrderPrice': min_price,
+        'maximumOrderPrice': max_price,
         'type': ShippingMethodTypeEnum.PRICE.name}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingPriceCreate']['shippingMethod']
     assert 'errors' not in data
@@ -282,23 +274,95 @@ def test_create_shipping_method(
     assert data['type'] == ShippingMethodTypeEnum.PRICE.name
 
 
+@pytest.mark.parametrize(
+    'min_price, max_price, expected_error',
+    ((
+        None, 15.11, {
+            'field':
+            'minimumOrderPrice',
+            'message':
+            'Minimum order price is required'
+            ' for Price Based shipping.'}),
+     (
+         20.21, 15.11, {
+             'field': 'maximumOrderPrice',
+             'message':
+             'Maximum order price should be larger than the minimum.'})))
+def test_create_price_shipping_method_errors(
+        shipping_zone, staff_api_client, min_price, max_price, expected_error,
+        permission_manage_shipping):
+    query = PRICE_BASED_SHIPPING_QUERY
+    shipping_zone_id = graphene.Node.to_global_id(
+        'ShippingZone', shipping_zone.pk)
+    variables = {
+        'shippingZone': shipping_zone_id,
+        'name': 'DHL',
+        'price': 12.34,
+        'minimumOrderPrice': min_price,
+        'maximumOrderPrice': max_price,
+        'type': ShippingMethodTypeEnum.PRICE.name}
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
+    content = get_graphql_content(response)
+    data = content['data']['shippingPriceCreate']
+    assert data['errors'][0] == expected_error
+
+
+WEIGHT_BASED_SHIPPING_QUERY = """
+    mutation createShipipngPrice(
+        $type: ShippingMethodTypeEnum, $name: String!, $price: Decimal,
+        $shippingZone: ID!, $maximumOrderWeight: WeightScalar,
+        $minimumOrderWeight: WeightScalar) {
+        shippingPriceCreate(
+            input: {
+                name: $name, price: $price, shippingZone: $shippingZone,
+                minimumOrderWeight:$minimumOrderWeight,
+                maximumOrderWeight: $maximumOrderWeight, type: $type}) {
+            errors {
+                field
+                message
+            }
+            shippingMethod {
+                minimumOrderWeight {
+                    value
+                    unit
+                }
+                maximumOrderWeight {
+                    value
+                    unit
+                }
+            }
+        }
+    }
+"""
+
+
 @pytest.mark.parametrize(
     'min_weight, max_weight, expected_min_weight, expected_max_weight',
-    (
-        (10.32, 15.64, {'value': 10.32, 'unit': 'kg'},
-         {'value': 15.64, 'unit': 'kg'}),
-        (10.92, None, {'value': 10.92, 'unit': 'kg'}, None)))
+    ((
+        10.32, 15.64, {
+            'value': 10.32,
+            'unit': 'kg'}, {
+                'value': 15.64,
+                'unit': 'kg'}),
+     (10.92, None, {
+         'value': 10.92,
+         'unit': 'kg'}, None)))
 def test_create_weight_based_shipping_method(
-        shipping_zone, admin_api_client, min_weight, max_weight,
-        expected_min_weight, expected_max_weight, weight_based_shipping_query):
-    query = weight_based_shipping_query
+        shipping_zone, staff_api_client, min_weight, max_weight,
+        expected_min_weight, expected_max_weight, permission_manage_shipping):
+    query = WEIGHT_BASED_SHIPPING_QUERY
     shipping_zone_id = graphene.Node.to_global_id(
         'ShippingZone', shipping_zone.pk)
     variables = {
-        'shippingZone': shipping_zone_id, 'name': 'DHL', 'price': 12.34,
-        'minimumOrderWeight': min_weight, 'maximumOrderWeight': max_weight,
+        'shippingZone': shipping_zone_id,
+        'name': 'DHL',
+        'price': 12.34,
+        'minimumOrderWeight': min_weight,
+        'maximumOrderWeight': max_weight,
         'type': ShippingMethodTypeEnum.WEIGHT.name}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingPriceCreate']['shippingMethod']
     assert data['minimumOrderWeight'] == expected_min_weight
@@ -308,58 +372,44 @@ def test_create_weight_based_shipping_method(
 @pytest.mark.parametrize(
     'min_weight, max_weight, expected_error',
     (
-        (None, 15.11, {
-            'field': 'minimumOrderWeight',
-            'message': 'Minimum order weight is required for'
-                       ' Weight Based shipping.'}),
-        (20.21, 15.11, {
-            'field': 'maximumOrderWeight',
-            'message': 'Maximum order weight should be larger than the minimum.'  # noqa
-        })))
+        (
+            None, 15.11, {
+                'field':
+                'minimumOrderWeight',
+                'message':
+                'Minimum order weight is required for'
+                ' Weight Based shipping.'}),
+        (
+            20.21,
+            15.11,
+            {
+                'field':
+                'maximumOrderWeight',
+                'message':
+                'Maximum order weight should be larger than the minimum.'  # noqa
+            })))
 def test_create_weight_shipping_method_errors(
-        shipping_zone, admin_api_client, min_weight, max_weight,
-        expected_error, weight_based_shipping_query):
-    query = weight_based_shipping_query
+        shipping_zone, staff_api_client, min_weight, max_weight,
+        expected_error, permission_manage_shipping):
+    query = WEIGHT_BASED_SHIPPING_QUERY
     shipping_zone_id = graphene.Node.to_global_id(
         'ShippingZone', shipping_zone.pk)
     variables = {
-        'shippingZone': shipping_zone_id, 'name': 'DHL', 'price': 12.34,
-        'minimumOrderWeight': min_weight, 'maximumOrderWeight': max_weight,
+        'shippingZone': shipping_zone_id,
+        'name': 'DHL',
+        'price': 12.34,
+        'minimumOrderWeight': min_weight,
+        'maximumOrderWeight': max_weight,
         'type': ShippingMethodTypeEnum.WEIGHT.name}
-    response = admin_api_client.post_graphql(query, variables)
-    content = get_graphql_content(response)
-    data = content['data']['shippingPriceCreate']
-    assert data['errors'][0] == expected_error
-
-
-@pytest.mark.parametrize(
-    'min_price, max_price, expected_error',
-    (
-        (None, 15.11, {
-            'field': 'minimumOrderPrice',
-            'message': 'Minimum order price is required'
-                       ' for Price Based shipping.'}),
-        (20.21, 15.11, {
-            'field': 'maximumOrderPrice',
-            'message': 'Maximum order price should be larger than the minimum.'
-        })))
-def test_create_price_shipping_method_errors(
-        shipping_zone, admin_api_client, min_price, max_price,
-        expected_error, price_based_shipping_query):
-    query = price_based_shipping_query
-    shipping_zone_id = graphene.Node.to_global_id(
-        'ShippingZone', shipping_zone.pk)
-    variables = {
-        'shippingZone': shipping_zone_id, 'name': 'DHL', 'price': 12.34,
-        'minimumOrderPrice': min_price, 'maximumOrderPrice': max_price,
-        'type': ShippingMethodTypeEnum.PRICE.name}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingPriceCreate']
     assert data['errors'][0] == expected_error
 
 
-def test_update_shipping_method(admin_api_client, shipping_zone):
+def test_update_shipping_method(
+        staff_api_client, shipping_zone, permission_manage_shipping):
     query = """
     mutation updateShippingPrice(
         $id: ID!, $price: Decimal, $shippingZone: ID!,
@@ -397,13 +447,15 @@ def test_update_shipping_method(admin_api_client, shipping_zone):
         'id': shipping_method_id,
         'minimumOrderPrice': 12.00,
         'type': ShippingMethodTypeEnum.PRICE.name}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingPriceUpdate']['shippingMethod']
     assert data['price']['amount'] == float(price)
 
 
-def test_delete_shipping_method(admin_api_client, shipping_method):
+def test_delete_shipping_method(
+        staff_api_client, shipping_method, permission_manage_shipping):
     query = """
         mutation deleteShippingPrice($id: ID!) {
             shippingPriceDelete(id: $id) {
@@ -418,7 +470,8 @@ def test_delete_shipping_method(admin_api_client, shipping_method):
     shipping_method_id = graphene.Node.to_global_id(
         'ShippingMethod', shipping_method.pk)
     variables = {'id': shipping_method_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_shipping])
     content = get_graphql_content(response)
     data = content['data']['shippingPriceDelete']['shippingMethod']
     assert data['price']['amount'] == float(shipping_method.price.amount)
diff --git a/tests/api/test_shop.py b/tests/api/test_shop.py
--- a/tests/api/test_shop.py
+++ b/tests/api/test_shop.py
@@ -9,7 +9,7 @@
 
 
 def test_query_authorization_keys(
-        authorization_key, admin_api_client, user_api_client):
+        authorization_key, staff_api_client, permission_manage_settings):
     query = """
     query {
         shop {
@@ -20,15 +20,13 @@ def test_query_authorization_keys(
         }
     }
     """
-    response = admin_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_settings])
     content = get_graphql_content(response)
     data = content['data']['shop']
     assert data['authorizationKeys'][0]['name'] == authorization_key.name
     assert data['authorizationKeys'][0]['key'] == authorization_key.key
 
-    response = user_api_client.post_graphql(query)
-    assert_no_permission(response)
-
 
 def test_query_countries(user_api_client):
     query = """
@@ -116,7 +114,7 @@ def test_query_languages(settings, user_api_client):
     assert len(data['languages']) == len(settings.LANGUAGES)
 
 
-def test_query_permissions(admin_api_client, user_api_client):
+def test_query_permissions(staff_api_client, permission_manage_settings):
     query = """
     query {
         shop {
@@ -127,7 +125,8 @@ def test_query_permissions(admin_api_client, user_api_client):
         }
     }
     """
-    response = admin_api_client.post_graphql(query)
+    response = staff_api_client.post_graphql(
+        query, permissions=[permission_manage_settings])
     content = get_graphql_content(response)
     data = content['data']['shop']
     permissions = data['permissions']
@@ -136,9 +135,6 @@ def test_query_permissions(admin_api_client, user_api_client):
     for code in permissions_codes:
         assert code in MODELS_PERMISSIONS
 
-    response = user_api_client.post_graphql(query)
-    assert_no_permission(response)
-
 
 def test_query_navigation(user_api_client, site_settings):
     query = """
@@ -162,7 +158,8 @@ def test_query_navigation(user_api_client, site_settings):
     assert navigation_data['secondary']['name'] == site_settings.bottom_menu.name
 
 
-def test_shop_settings_mutation(admin_api_client, site_settings):
+def test_shop_settings_mutation(
+        staff_api_client, site_settings, permission_manage_settings):
     query = """
         mutation updateSettings($input: ShopSettingsInput!) {
             shopSettingsUpdate(input: $input) {
@@ -177,7 +174,8 @@ def test_shop_settings_mutation(admin_api_client, site_settings):
         'input': {
             'includeTaxesInPrices': False,
             'headerText': 'Lorem ipsum'}}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_settings])
     content = get_graphql_content(response)
     data = content['data']['shopSettingsUpdate']['shop']
     assert data['includeTaxesInPrices'] == False
@@ -186,7 +184,7 @@ def test_shop_settings_mutation(admin_api_client, site_settings):
     assert not site_settings.include_taxes_in_prices
 
 
-def test_shop_domain_update(admin_api_client):
+def test_shop_domain_update(staff_api_client, permission_manage_settings):
     query = """
         mutation updateSettings($input: SiteDomainInput!) {
             shopDomainUpdate(input: $input) {
@@ -206,7 +204,8 @@ def test_shop_domain_update(admin_api_client):
             'name': new_name}}
     site = Site.objects.get_current()
     assert site.domain != 'lorem-ipsum.com'
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_settings])
     content = get_graphql_content(response)
     data = content['data']['shopDomainUpdate']['shop']
     assert data['domain']['host'] == 'lorem-ipsum.com'
@@ -216,7 +215,8 @@ def test_shop_domain_update(admin_api_client):
     assert site.name == new_name
 
 
-def test_homepage_collection_update(admin_api_client, collection):
+def test_homepage_collection_update(
+        staff_api_client, collection, permission_manage_settings):
     query = """
         mutation homepageCollectionUpdate($collection: ID!) {
             homepageCollectionUpdate(collection: $collection) {
@@ -231,7 +231,8 @@ def test_homepage_collection_update(admin_api_client, collection):
     """
     collection_id = graphene.Node.to_global_id('Collection', collection.id)
     variables = {'collection': collection_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_settings])
     content = get_graphql_content(response)
     data = content['data']['homepageCollectionUpdate']['shop']
     assert data['homepageCollection']['id'] == collection_id
diff --git a/tests/api/test_variant.py b/tests/api/test_variant.py
--- a/tests/api/test_variant.py
+++ b/tests/api/test_variant.py
@@ -4,7 +4,7 @@
 from tests.api.utils import get_graphql_content
 
 
-def test_fetch_variant(admin_api_client, product):
+def test_fetch_variant(staff_api_client, product, permission_manage_products):
     query = """
     query ProductVariantDetails($id: ID!) {
         productVariant(id: $id) {
@@ -52,13 +52,15 @@ def test_fetch_variant(admin_api_client, product):
     variant = product.variants.first()
     variant_id = graphene.Node.to_global_id('ProductVariant', variant.pk)
     variables = {'id': variant_id}
-    response = admin_api_client.post_graphql(query, variables)
+    staff_api_client.user.user_permissions.add(permission_manage_products)
+    response = staff_api_client.post_graphql(query, variables)
     content = get_graphql_content(response)
     data = content['data']['productVariant']
     assert data['name'] == variant.name
 
 
-def test_create_variant(admin_api_client, product, product_type):
+def test_create_variant(
+        staff_api_client, product, product_type, permission_manage_products):
     query = """
         mutation createVariant (
             $productId: ID!,
@@ -130,7 +132,8 @@ def test_create_variant(admin_api_client, product, product_type):
         'attributes': [
             {'slug': variant_slug, 'value': variant_value}],
         'trackInventory': True}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productVariantCreate']['productVariant']
     assert data['name'] == variant_value
@@ -145,7 +148,8 @@ def test_create_variant(admin_api_client, product, product_type):
 
 
 def test_create_product_variant_not_all_attributes(
-        admin_api_client, product, product_type, color_attribute):
+        staff_api_client, product, product_type, color_attribute,
+        permission_manage_products):
     query = """
             mutation createVariant (
                 $productId: ID!,
@@ -175,14 +179,17 @@ def test_create_product_variant_not_all_attributes(
         'productId': product_id,
         'sku': sku,
         'attributes': [{'slug': variant_slug, 'value': variant_value}]}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     assert content['data']['productVariantCreate']['errors']
-    assert content['data']['productVariantCreate']['errors'][0]['field'] == 'attributes:color'
+    assert content['data']['productVariantCreate']['errors'][0]['field'] == (
+        'attributes:color')
     assert not product.variants.filter(sku=sku).exists()
 
 
-def test_update_product_variant(admin_api_client, product):
+def test_update_product_variant(
+        staff_api_client, product, permission_manage_products):
     query = """
         mutation updateVariant (
             $id: ID!,
@@ -225,7 +232,8 @@ def test_update_product_variant(admin_api_client, product):
         'costPrice': cost_price,
         'trackInventory': True}
 
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     variant.refresh_from_db()
     content = get_graphql_content(response)
     data = content['data']['productVariantUpdate']['productVariant']
@@ -236,7 +244,8 @@ def test_update_product_variant(admin_api_client, product):
 
 
 def test_update_product_variant_not_all_attributes(
-        admin_api_client, product, product_type, color_attribute):
+        staff_api_client, product, product_type, color_attribute,
+        permission_manage_products):
     query = """
         mutation updateVariant (
             $id: ID!,
@@ -268,15 +277,17 @@ def test_update_product_variant_not_all_attributes(
         'sku': sku,
         'attributes': [{'slug': variant_slug, 'value': variant_value}]}
 
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     variant.refresh_from_db()
     content = get_graphql_content(response)
     assert content['data']['productVariantUpdate']['errors']
-    assert content['data']['productVariantUpdate']['errors'][0]['field'] == 'attributes:color'
+    assert content['data']['productVariantUpdate']['errors'][0]['field'] == (
+        'attributes:color')
     assert not product.variants.filter(sku=sku).exists()
 
 
-def test_delete_variant(admin_api_client, product):
+def test_delete_variant(staff_api_client, product, permission_manage_products):
     query = """
         mutation variantDelete($id: ID!) {
             productVariantDelete(id: $id) {
@@ -290,7 +301,8 @@ def test_delete_variant(admin_api_client, product):
     variant = product.variants.first()
     variant_id = graphene.Node.to_global_id('ProductVariant', variant.pk)
     variables = {'id': variant_id}
-    response = admin_api_client.post_graphql(query, variables)
+    response = staff_api_client.post_graphql(
+        query, variables, permissions=[permission_manage_products])
     content = get_graphql_content(response)
     data = content['data']['productVariantDelete']
     assert data['productVariant']['sku'] == variant.sku
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,4 +1,3 @@
-import json
 from io import BytesIO
 from unittest.mock import MagicMock, Mock
 
@@ -9,13 +8,11 @@
 from django.core.files import File
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.forms import ModelForm
-from django.shortcuts import reverse
-from django.test.client import MULTIPART_CONTENT, Client
+from django.test.client import Client
 from django.utils.encoding import smart_text
 from django_countries import countries
 from django_prices_vatlayer.models import VAT
 from django_prices_vatlayer.utils import get_tax_for_rate
-from graphql_jwt.shortcuts import get_token
 from payments import FraudStatus, PaymentStatus
 from PIL import Image
 from prices import Money
@@ -38,75 +35,6 @@
     ShippingMethod, ShippingMethodType, ShippingZone)
 from saleor.site.models import AuthorizationKey, SiteSettings
 
-API_PATH = reverse('api')
-
-
-class ApiClient(Client):
-    """GraphQL API client."""
-
-    def __init__(self, *args, **kwargs):
-        user = kwargs.pop('user')
-        self.user = user
-        self.token = get_token(user)
-        super().__init__(*args, **kwargs)
-
-    def _base_environ(self, **request):
-        environ = super()._base_environ(**request)
-        environ.update({'HTTP_AUTHORIZATION': 'JWT %s' % self.token})
-        return environ
-
-    def post(self, data=None, **kwargs):
-        """Send a POST request.
-
-        This wrapper sets the `application/json` content type which is
-        more suitable for standard GraphQL requests and doesn't mismatch with
-        handling multipart requests in Graphene.
-        """
-        if data:
-            data = json.dumps(data)
-        kwargs['content_type'] = 'application/json'
-        return super().post(API_PATH, data, **kwargs)
-
-    def post_graphql(self, query=None, variables=None, **kwargs):
-        """Dedicated helper for posting GraphQL queries.
-
-        Sets the `application/json` content type and json.dumps the variables
-        if present.
-        """
-        data = {}
-        if query is not None:
-            data['query'] = query
-        if variables is not None:
-            data['variables'] = json.dumps(variables)
-        if data:
-            data = json.dumps(data)
-        kwargs['content_type'] = 'application/json'
-        return super().post(API_PATH, data, **kwargs)
-
-    def post_multipart(self, *args, **kwargs):
-        """Send a multipart POST request.
-
-        This is used to send multipart requests to GraphQL API when e.g.
-        uploading files.
-        """
-        kwargs['content_type'] = MULTIPART_CONTENT
-        return super().post(API_PATH, *args, **kwargs)
-
-
-@pytest.fixture
-def admin_api_client(admin_user):
-    return ApiClient(user=admin_user)
-
-
-@pytest.fixture
-def user_api_client(customer_user):
-    return ApiClient(user=customer_user)
-
-
-@pytest.fixture
-def staff_api_client(staff_user):
-    return ApiClient(user=staff_user)
-
 
 @pytest.fixture(autouse=True)
 def site_settings(db, settings):
@@ -615,6 +543,11 @@ def permission_manage_menus():
     return Permission.objects.get(codename='manage_menus')
 
 
+@pytest.fixture
+def permission_manage_pages():
+    return Permission.objects.get(codename='manage_pages')
+
+
 @pytest.fixture
 def collection(db):
     collection = Collection.objects.create(