Есть ли способ имитировать .strip() для модульного тестирования в модуле unittest Python 2.7?

Я использую 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() уже имеет дело с посторонними пробелами? Я новичок в модульном тестировании и даже новичок в концепции насмешек, поэтому буду признателен за любую помощь.

Имейте в виду, что Python 2.7 не поддерживается, даже для существенных исправлений безопасности, с 1 января 2020 года. Он примерно такой же старый, как Windows 7. Если Python 2.7 поставляется с вашим компьютером, не пытайтесь удалить его, так как это может серьезно повредить вашу операционную систему; но, пожалуйста, попробуйте написать код для актуальной версии Python, убедитесь, что она у вас установлена, и убедитесь, что вы можете обеспечить запуск кода с этой версией. (Ничто из того, что я здесь вижу, не зависит от версии 2.x.)

Karl Knechtel 17.06.2024 18:50

Не издевайтесь str.strip. Mock os.path.exists(), чтобы он возвращал False всякий раз, когда имя начинается или заканчивается пробелом.

Barmar 17.06.2024 18:56

@KarlKnechtel «Бегун» предназначен для кода, использующего пакет Python, существующий только для версии 2.7, о которой я упоминал в начале. Эта функция не может быть отделена от этого кода, поэтому мне придется использовать версию 2.7. Я дважды проверил: для программы, для которой я пишу сценарий, не существует пакета Python 3. У меня нет выбора.

Réka 17.06.2024 18:59

@Barmar Тот же результат, что и в примере 2, похоже, возвращаемое значение не обновляется во время вызова input_check.

Réka 17.06.2024 19:01
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте side_effect вместо os.path.exists; не издевайся strip()

Я предлагаю вам использовать атрибут side_effect (документацию см. здесь) Mock-объекта patchexist вместо return_value.
Таким образом, вы можете вернуть False при первом вызове os.path.exists() и True при втором вызове (после strip()).

Более того:

Функцию strip() патчить не обязательно.

Питон 3 и Питон 2.7

Я тестировал код на 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, и это имеет большой смысл. Это будет очень полезно!

Réka 24.06.2024 19:33

С помощью side_effect() вы можете выбрать, что возвращает функция/метод после первого, второго, третьего... выполнения. Все эти значения необходимо вставить внутри списка в желаемом порядке. В вашем случае вам необходимо установить другое возвращаемое значение выполнения patchexist.

User051209 25.06.2024 08:20

Другие вопросы по теме