Я использую Python 2.7 для проекта кодирования. Мне бы хотелось перейти на Python 3, но, к сожалению, я пишу сценарии для программы, которая имеет только пакет Python в версии 2.7, и даже если бы у нее был один из трех, нашу кодовую базу было бы непрактично переключить, так что это невозможно.
Мой код включает в себя проверку существования пути, заданного в строковой форме, а затем, поскольку я не знаю, делает ли это сам os.path.exists, если нет, он запускает .strip() для имени файла и повторяет попытку. .
Я пытаюсь провести модульное тестирование этого. Я провел тест на то, что он вообще не существует, исправив os.path.exists, чтобы он возвращал False. Но я не могу понять, как выполнить модульное тестирование в случае, когда он возвращает False до .strip() и True после.
Вот соответствующая часть тестируемой функции (if/elif/else относится к модульным тестам):
import os
class Runner:
def __init__(self, fname):
self.fname = fname
def input_check(self):
if not os.path.exists(self.fname):
self.fname = self.fname.strip()
if not os.path.exists(self.fname):
raise ValueError('input is not a valid path')
if os.path.isfile(self.fname):
self.ftype = 'file'
elif os.path.isdir(self.fname):
self.ftype = 'folder'
else:
raise ValueError('how is your input neither a file nor a folder??')
И два примера того, что я пробовал для модульного тестирования: Пример 1
import unittest
from mock import patch
class TestRunner(unittest.TestCase):
@patch('.strip')
@patch('os.path.exists')
def test_input_check_exists_after_strip(self, patchexist, patchstrip):
runner = Runner('test ')
patchstrip.return_value = 'test'
patchexist.return_value = False if runner.fname[-1] == ' ' else True
with self.assertRaisesRegexp(ValueError, 'how is your input neither a file nor a folder??'):
runner.input_check()
Кажется, я не могу понять, как заставить его на самом деле исправлять .strip, и ответы, которые я нашел через Google, похоже, говорят, что нет способа исправить определенные встроенные функции (я также пробовал встроенные функции. тоже не работает.) Там написано пустое имя модуля или нет модуля с именем встроенные.
Пример 2
import unittest
from mock import patch
class TestRunner(unittest.TestCase):
@patch('os.path.exists')
def test_input_check_exists_after_strip(self, patchexist):
runner = Runner('test')
patchexist.return_value = patchexist.called
with self.assertRaisesRegexp(ValueError, 'how is your input neither a file nor a folder??'):
runner.input_check()
Этот возвращается с сообщением «Ввод не является допустимым путем» ValueError. Я предполагаю, что возвращаемое значение патча просто не обновляется во время выполнения input_check(), что имеет смысл, даже если мне это неудобно.
Есть ли способ проверить это? Это вообще необходимо, или os.path.exists() уже имеет дело с посторонними пробелами? Я новичок в модульном тестировании и даже новичок в концепции насмешек, поэтому буду признателен за любую помощь.
Не издевайтесь str.strip. Mock os.path.exists(), чтобы он возвращал False всякий раз, когда имя начинается или заканчивается пробелом.
@KarlKnechtel «Бегун» предназначен для кода, использующего пакет Python, существующий только для версии 2.7, о которой я упоминал в начале. Эта функция не может быть отделена от этого кода, поэтому мне придется использовать версию 2.7. Я дважды проверил: для программы, для которой я пишу сценарий, не существует пакета Python 3. У меня нет выбора.
@Barmar Тот же результат, что и в примере 2, похоже, возвращаемое значение не обновляется во время вызова input_check.






side_effect вместо os.path.exists; не издевайся strip()Я предлагаю вам использовать атрибут side_effect (документацию см. здесь) Mock-объекта patchexist вместо return_value.
Таким образом, вы можете вернуть False при первом вызове os.path.exists() и True при втором вызове (после strip()).
Более того:
Функцию
strip()патчить не обязательно.
Я тестировал код на Python 3, а не на Python 2.7; Я думаю, вам легко адаптировать его для Python 2.7; например:
from unittest.mock import patch
в Python 2.7 становится:
from mock import patch
Дополнительную информацию о разнице между Python 3 и Python 2.7 см. в этом посте.
Ниже я покажу вам тестовый код, который содержит 3 теста (это 3 тестовых случая):
test_input_check_exists_after_strip(): файл test и файл test не существуют (ваш код вызывает исключение ValueError)test_file_exist_after_strip(): файл test не существует, но файл test (после strip()) существуетtest_directory_exist_after_strip(): файл test не существует, но каталог test (после strip()) существуетimport unittest
from runner import Runner
from unittest.mock import patch
import os
class TestRunner(unittest.TestCase):
@patch('os.path.exists')
def test_input_check_exists_after_strip(self, patchexist):
# the file `test ` doesn't exist; the file `test` doesn't exist
patchexist.side_effect = [False, False]
runner = Runner('test ')
with self.assertRaises(ValueError):
runner.input_check()
@patch('os.path.isfile')
@patch('os.path.exists')
def test_file_exist_after_strip(self, patchexist, patchisfile):
# the file `test ` doesn't exist; the file `test` exist
patchexist.side_effect = [False, True]
patchisfile.return_value = True
runner = Runner('test ')
runner.input_check()
self.assertEqual('file', runner.ftype)
@patch('os.path.isdir')
@patch('os.path.isfile')
@patch('os.path.exists')
def test_directory_exist_after_strip(self, patchexist, patchisfile, patchisdir):
# the file `test ` doesn't exist; the directory `test` exist
patchexist.side_effect = [False, True]
patchisfile.return_value = False
patchisdir.return_value = True
runner = Runner('test ')
runner.input_check()
self.assertEqual('folder', runner.ftype)
if __name__ == '__main__':
unittest.main()
Выполнение всех тестов вызывает настоящий метод strip(), не имитируя его.
Ниже результат выполнения тестов в моей системе:
...
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
В итоге я исправил это, просто переместив .strip() перед первой проверкой .exists() — но спасибо! Я не до конца понимал, как работает .side_effect, и это имеет большой смысл. Это будет очень полезно!
С помощью side_effect() вы можете выбрать, что возвращает функция/метод после первого, второго, третьего... выполнения. Все эти значения необходимо вставить внутри списка в желаемом порядке. В вашем случае вам необходимо установить другое возвращаемое значение выполнения patchexist.
Имейте в виду, что Python 2.7 не поддерживается, даже для существенных исправлений безопасности, с 1 января 2020 года. Он примерно такой же старый, как Windows 7. Если Python 2.7 поставляется с вашим компьютером, не пытайтесь удалить его, так как это может серьезно повредить вашу операционную систему; но, пожалуйста, попробуйте написать код для актуальной версии Python, убедитесь, что она у вас установлена, и убедитесь, что вы можете обеспечить запуск кода с этой версией. (Ничто из того, что я здесь вижу, не зависит от версии 2.x.)