A
A
Alexey2020-04-01 12:04:08
Python
Alexey, 2020-04-01 12:04:08

How to test service A, which accesses service B, without raising service B locally (Python, Flask)?

The task is known, but I can not find a solution in my implementation.

We have the following. Service A does some useful work. Service B is an authenticator that, upon request from A, confirms the right to this appeal. Those. when service C requests service A, the latter contacts B and specifies the validity of the authorization token that service C "showed" to service A.
You need to write tests for A, replacing the call to service B with a stub. Here is the schematic code for service A.

application/api.py

from application.core inport auth

@main.route('/get_data', methods=['GET'])
@auth
def get_data():
    return '{"data": "Any data"}'


Decorator that checks the validity of the authorization token:

application/core.py
def auth(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        token = request.headers.get('X-Auth-Token')
        if not get_from_cache(token):
            res = requests.get('https://service_b.com/api/check_token?token=' + token)
            if res.status_code == 200:
                set_to_cache(token)
                return fn(*args, **kwargs)

            return Responce('{"error": "Invalid token"}', status=404)

    return wrapper


Test for A (based on pytest):

def test_get_data(client):
    rs = client.get("get_data", headers={'X-Auth-Token': 'some_invalid_token'})
    assert rs.status_code == 404
    rs = client.get("get_data", headers={'X-Auth-Token': 'some_valid_token'})
    assert rs.status_code == 200


Is it possible to mock the service_b.com/api/check_token call in the decorator without making any changes to the decorator code?

Answer the question

In order to leave comments, you need to log in

4 answer(s)
A
Alexey, 2020-04-01
@alenov

I solved the problem by conditional importing.

import importlib

def auth(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        # В переменной конфигурации EXT_API_MODULE хранится имя модуля, который содержит класс extAPI
        # для генерации вызовов к внешним API. Модулей два: ext_api.py и exp_api_test.py
        # В первом реальные обработчики для продакшена, EXT_API_MODULE="exp_api"
        # Во втором - заглушки для тестов, EXT_API_MODULE="exp_api_test"
        ext_api = importlib.import_module('application.{0}'.format(current_app.config['EXT_API_MODULE']))
        token = request.headers.get('X-Auth-Token')
        if not get_from_cache(token):
            res = ext_api.extAPI().auth_service(token)
            if res.status_code == 200:
                set_to_cache(token)
                return fn(*args, **kwargs)

            return Responce('{"error": "Invalid token"}', status=404)

    return wrapper

Thank you all for participating!

V
Vladimir Kuts, 2020-04-01
@fox_12

On projects that work closely with external APIs, I used a mock server in a separate docker container that mimics real external API responses to create tests:
wiremock.org

D
Dimonchik, 2020-04-01
@dimonchik2013

httpbin with stubs, or some kind of mock server

D
Dr. Bacon, 2020-04-01
@bacon

What if you do ? patch('application.core.requests.get')

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question