Как смоделировать flask.request с помощью unittest? - PullRequest
0 голосов
/ 18 июня 2019

В моем приложении у меня есть класс, который вызывается из службы фляги.Этот класс берет некоторые атрибуты из объекта flask.request, поэтому я хочу их смоделировать.

Пример моей реализации:

myClassHelper.py

from flask import request

class MyClassHelper:

    def __init__(self, addRequestData=False):
        self.attribute = 'something'
        self.path = request.path if addRequestData else None

    def __str__(self):
        return 'attribute={0}; path={1};'.format(self.attribute, self.path)

myClassHelperTest.py

from unittest import TestCase
from unittest.mock import MagicMock

import flask

from myClassHelper import MyClassHelper


class MyClassHelperTest(TestCase):
    def setUp(self):
        self.path = '/path'

        self.unmock = {}
        self.unmock['flask.request'] = flask.request

        flask.request = MagicMock(path='/path')


    def tearDown(self):
        flask.request = self.unmock['flask.request']

    def test_printAttributes(self):
        expectedResult = 'attribute=something; path={0};'.format(self.path)
        result = str(MyClassHelper(addRequestData=True))
        self.assertEqual(expectedResult, result)

Проблема возникает, когда я выполняю импорт from myClassHelper import MyClassHelper.Это идет к импорту from flask import request внутри MyClassHelper.Так что макет в методе setUp тестового класса, он не применяется.

Это можно решить, просто импортировав flask и получив доступ к атрибуту пути, например flask.request.path.Но я бы не хотел импортировать модуль полной колбы.

Есть ли способ создать модульный тест для метода, который использует атрибуты из flask.request, насмехается над ними и не использует клиент проверки колбы?

1 Ответ

0 голосов
/ 18 июня 2019

Должен быть способ, но код модульного тестирования, как этот, все равно вызовет у вас проблемы.SUT получает доступ к глобальному состоянию, которым управляет другой модуль, таким образом, ваши тесты должны правильно настроить это глобальное состояние, либо используя этот другой модуль как есть, что вам не нужно по уважительным причинам (плюс это не будетмодульное тестирование больше), или путем его обезьяньего исправления, которое часто бывает хитрым (как вы уже выяснили) и ломким (ваши тесты сломаются, если вы измените способ импорта вещей в рабочий код; почему это должно произойти, если соответствующиеповедение не изменилось?)

Исправление для такого рода проблем - заставить ваши объекты запрашивать то, что им нужно, вместо того, чтобы искать их в глобальном состоянии.Поэтому, если все, что нужно экземпляру MyClassHelper, это путь, просто попросите его указать путь.Пусть вызывающий код определит, откуда должен идти путь.В частности, ваши тесты могут легко обеспечить постоянные пути.

Так будет выглядеть ваш тест, если вы будете следовать этому принципу:

class MyClassHelperTest(TestCase):
    def test_printAttributes(self):
        expectedResult = 'attribute=something; path=/path;'
        result = str(MyClassHelper('/path'))
        self.assertEqual(expectedResult, result)

Гораздо проще, чем раньше.И вот как вы делаете это:

class MyClassHelper:
    def __init__(self, path):
        self.attribute = 'something'
        self.path = path

    def __str__(self):
        return 'attribute={0}; path={1};'.format(self.attribute, self.path)

Вам на самом деле не нужен attribute если поведение в тесте - это все, что вам нужно, я оставил его там, чтобы меньше отклоняться от вашего исходного кода,Я предполагаю, что у вас есть другие тесты, которые показывают, почему это действительно необходимо.

...