Как использовать тест черного ящика с stdin / stdout с python

Мне нравится TDD, поэтому я сначала стараюсь написать свой Black Box Test.

Это программа на Python, которая имеет дело с stdin и выводит в stdout вот так (я пытаюсь написать свой собственный язык, который работает только с stdin и stdout):

$ python3 ./minor.py
>>> print, "hello\nthis is a good morning"
... hello
  . this is a good morning
>>> $quit

Но я не могу издеваться над stdin и stdout. Я пытаюсь использовать subprocess в Python, но Popen.stdout.read() висит из-за EOF, для чего нужно убить программу. Или communicate(), но он убьет мою программу и не сможет обрабатывать два или более ввода.

Это расстроило меня на 2+ дня, я не могу найти ничего полезного в тестировании имитацией или черным ящиком с помощью stdin / stdout (выглядит странно, что я могу легко протестировать с браузером, но не с stdin / stdout).

Спасибо.

*** First Editing ***

Я создаю новый класс unittest для обработки моего класса. У него есть функция для создания нового объекта Popen.

Я пытаюсь написать на stdin и подтвердить _11 _... Но он завис только потому, что не может найти EOF.

Как мне с этим справиться, чтобы это произошло? Спасибо за вашу помощь!

class TestFunc(unittest.TestCase):
    def run_minor(self):
        return Popen(['python3', './minor.py'],
                stdin = PIPE,
                stdout = PIPE,
                stderr = PIPE,
                text = True,
            )

    def test_print(self):
        prop = self.run_minor()

        self.assertEqual(prop.stdout.read(), '>>> ')

        prop.stdin.write("print, 'this'")
        self.assertEqual(prop.stdout.read(), '... this\n>>> ')

        prop.stdin.write("$quit")
        self.assertEqual(prop.stdout.read(), '')

        prop.kill()

person Peterlits Zo    schedule 20.08.2020    source источник
comment
Я не уверен, что понимаю, что вы имеете в виду, когда говорите, что не можете имитировать stdin и stdout - sys.stdin и sys.stdout являются просто файловыми объектами, вы можете заменить их другими файловыми объектами, которые ведут себя так, как того требуют ваши тесты. Есть случаи, когда вам действительно нужно переопределить FD 0 и FD 1, но os.dup2() быстро справляется с этими случаями.   -  person Charles Duffy    schedule 20.08.2020
comment
... итак, не могли бы вы показать нам, что вы сделали, чтобы попытаться имитировать stdin или stdout, и явный режим отказа, с которым вы столкнулись при попытке?   -  person Charles Duffy    schedule 20.08.2020
comment
@CharlesDuffy Хорошо, я редактирую ...   -  person Peterlits Zo    schedule 20.08.2020
comment
Отвечает ли это на ваш вопрос? Python 3 unittest имитирует ввод данных пользователем   -  person Evgeny    schedule 21.08.2020
comment
@Evgeny, Нет и да ... Я думаю из-за твоего комментария, и не могу поиздеваться над другой функцией! Просто потому, что мне нужно протестировать это, а не высмеивать. Я хочу поиздеваться над input/output. Кстати, я думаю, подходит ли мне использование функции для обработки ввода / вывода, такой как строка ... ввод, затем превращайте его в строку и возвращайте строку, затем вывод ... Или создайте класс с состоянием.   -  person Peterlits Zo    schedule 21.08.2020
comment
Ваше второе изменение выглядит как что-то, что следует добавить в качестве ответа, а не редактирования вопроса. (Ответить на свои вопросы вполне приемлемо!)   -  person Charles Duffy    schedule 21.08.2020
comment
(Кроме того, здесь нет причин использовать маркеры редактирования - цель должна состоять в том, чтобы сделать ваш вопрос как можно более понятным для людей, которые видят его в первый раз; людей, которые взаимодействовали с ним раньше и хотят увидеть различия всегда можно просто проверить в истории редактирования).   -  person Charles Duffy    schedule 21.08.2020
comment
@CharlesDuffy, хорошо! Спасибо за совет ~   -  person Peterlits Zo    schedule 21.08.2020


Ответы (1)


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

class MinorMock(object):
    """
    The Mock of Minor Programme.

    Use method `input` and `assertOutput` to set the input and the output want. Use
    `assertInputMeetOutput` to check if the input is meet its output by unittest's object.
    """
    def __init__(self, test_obj):
        self.popen = Popen(['python3', 'minor.py'], stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True)
        self.input_text = ''
        self.output_text = ''
        self.err_text = ''
        self.test_obj = test_obj

    def input(self, text):
        self.input_text += text
        return self

    def assertOutput(self, text):
        self.output_text += text
        return self

    def assertError(self, text):
        self.err_text += text
        return self
    
    def assertInputMeetOutput(self):
        (out, err) = self.popen.communicate(self.input_text)
        self.test_obj.assertEqual(out, self.output_text)
        self.test_obj.assertEqual(err, self.err_text)
        self.popen.kill()

Добро пожаловать на другие ответы ...

person Peterlits Zo    schedule 21.08.2020