Возникли проблемы с имитацией запроса в методе класса с использованием диспетчера контекста исправлений в Python

#python #unit-testing #mocking

#python #модульное тестирование #издевательство

Вопрос:

Я пытаюсь выполнить модульное тестирование метода класса, который выполняет запрос API и возвращает файл json, который загружается в переменную в качестве словаря. Запрос возвращает файл json в соответствии с запросом. Еще один тест, который я хотел бы реализовать, заключается в том, что он обращается к правильной ссылке. Я использую patch в качестве диспетчера контекста с макетным модулем.

В общем, мое приложение snowReport.py обращается к API погоды, который возвращает weatherJson, а затем обращается к этому Json, чтобы определить, будет ли снег в прогнозе. Класс называется Resort, потому что он предназначен специально для горнолыжных курортов.

В моем модуле это моя __init__ функция.

 class Resort():
    # kwargs is created so the user can pass in "96hr", "realtime", and or "360min"
    def __init__(self, resortKey, *args):
        # Check if you are in the current directory, if not, set it to the current directory
        currentDir = os.getcwd()

        if currentDir != D_NAME:
            os.chdir(D_NAME)
        else:
            pass

        # Checks if the user enters arguments to initiate json files or not
        self.dataJSON = SKI_RESORT_JSON
        self.args = args

        if not args:
            raise Exception("Invalid arg passed. Function arguments must contain one of '360min' and/or '96hr' and/or 'realtime'") # Note there is a hidden arg that is called test that does not access the api to do a mock test

        # Opens json file to get location parameters
        with open(SKI_RESORT_JSON, "r") as f:
            resortDictList = json.load(f)
            resortDict = resortDictList[resortKey]

        self.name = resortDict["name"]
        self.lon = resortDict["lon"]
        self.lat = resortDict["lat"]
        self.country = resortDict["country"]

        self.weatherJsonRealTime = {}
        self.weatherJson360Min = {}
        self.weatherJson96hr = {}
 

Цель этой __init_ функции _ — инициализировать переменные и получить доступ к файлу .json, где он извлекает данные о местоположении.

Функция, которую я пытаюсь протестировать, представляет собой метод класса, который выглядит следующим образом:

 def requestRealtime(self):
        querystring = {
            "lat": str(self.lat),
            "lon": str(self.lon),
            "unit_system": "si",
            "fields": "precipitation,precipitation_type,temp,feels_like,wind_speed,wind_direction,sunrise,sunset,visibility,cloud_cover,cloud_base,weather_code",
            "apikey": CLIMACELL_KEY,
        }

        response = requests.request("GET", URL_REALTIME, params=querystring)

        if response.ok:
            self.weatherJsonRealTime = json.load(response.text)
        else:
            return "Bad response"

        self.nowTime = localTime(self.weatherJsonRealTime["observation_time"]["value"])
        self.nowTemp = self.weatherJsonRealTime["temp"]["value"]
        self.nowFeelsLike = self.weatherJsonRealTime["feels_like"]["value"]
        self.nowPrecipitation = self.weatherJsonRealTime["precipitation"]["value"]
        self.nowPrecipitationType = self.weatherJsonRealTime["precipitation_type"]["value"]
        self.nowWindSpeed = self.weatherJsonRealTime["wind_speed"]["value"]
        self.nowWindDirection = self.weatherJsonRealTime["wind_direction"]["value"]
        self.nowCloudCover = self.weatherJsonRealTime["cloud_cover"]["value"]

        return self.weatherJsonRealTime  
 

Я пытаюсь протестировать запрос, точнее, я пытаюсь протестировать этот запрос. Для этого я использую диспетчер контекста исправлений, чтобы имитировать request.requests и установить возвращаемое значение в качестве статического тестового файла json. Мой тестовый код выглядит следующим образом:

 with open(".\Resources\test_realtimeJson.json", "r") as f:
    testrealtimeDict = json.load(f)
    
@classmethod
    def setUpClass(cls):
        cls.testResort = snowReport.Resort("test_Location (Banff)", "96hr", "realtime", "360min")
        os.chdir(D_NAME) # Set the directory back to D_NAME because that the snowReport.Resort class changes it's class


    def test_requestRealtime(self):
        with patch("snowApp.snowReport.requests.request") as mocked_request:
            mocked_request.return_value.ok = True
            mocked_request.return_value.request = testrealtimeDict
            self.testResort.requestRealtime()
 

Предполагая, что макет работает, я хотел бы затем использовать функцию assertEqual для тестирования и проверки того, возвращают ли атрибуты, созданные в функции snowReport, ожидаемое значение на основе словаря, который я ему передаю.

Когда я запускаю тестовый скрипт, он выдает мне ошибку:

 File "c:userssteveappdatalocalprogramspythonpython39libjson__init__.py", line 339, in loads
    raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not MagicMock
 

Поскольку я имитирую запрос и устанавливаю возвращаемое значение для созданного мной testrealtimeDict, не следует ли self.weatherJsonRealtime = testrealtimeDict ? Почему он выдает ошибку типа? Также — подходит ли модульный тест, который я планирую, для этого приложения или есть лучший или более простой способ выполнить это?