Как проверить программы Python для начинающих Python, которые используют input () (может быть, с Unittest?)?

StackOverflow https://stackoverflow.com/questions/4865195

Вопрос

Я грейдер для начального класса программирования с использованием Python. Мой Python-Fu сам не такой сильный, но я хотел бы попытаться автоматизировать часть оценки.

Глядя в Интернете, мне нравится Пипюнит Testing Suite, хотя он, вероятно, немного подавлен для того, что я хочу.

Моя проблема в том, что я не уверен, как передать тестовые входные данные, которые я хочу для функций студента, поскольку они еще не используют аргументы командной строки или даже несколько функций, но получают ввод пользователя через input() функция

Глупый пример:

#/usr/bin/python3.1
# A silly example python program
def main():
    a = int(input("Enter an integer: "))
    b = int(input("Enter another integer: "))
    c = a+b
    print("The sum is %d" % c)

if __name__ == '__main__'
    main()

Для моего глупого примера, как бы я напишет модульный тест, который мог бы проверить выход для нескольких различных входов? (т.е., если я передам 2 и 3 на вход, выходная строка должна быть «сумма 5»)

Это было полезно?

Решение

РЕДАКТИРОВАТЬ: только предложение об этом, так как пример не может быть невозможным (и я предполагаю, что студенты-начинающие будут просто смущены ограничением)

Если вы просто обеспокоены тем, что вы можете соответствовать тому, что вы ищете, почему бы просто не использовать какой -то «глупый» удар? Что-то типа:

echo -e "2\n3" | python test.py | grep -q "The sum is 5" && echo "Success"

Если вы делаете относительно тривиальные программы, подобные этому, то это должно быть достаточно или достаточно хорошим решением, которое требует мало усилий.

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

Вы не можете действительно испытать это. Одна из вещей о написании модульных тестов-это часто писать свой код по-разному, чтобы он был проверен на единицу. Таким образом, в этом случае вам нужно будет включить вызовы для ввода в отдельную функцию, которую вы можете затем исправить.

def my_input(prompt):
    return input(prompt)

def main():
    a = int(eval(my_input("Enter an integer: "))

и т.д. теперь ваш тест может обезьяна myscript.my_input Чтобы вернуть желаемые значения.

Если вам нужно более сложное взаимодействие с программами командной строки, чем может быть предоставлено с echo тогда вы можете посмотреть expect.

От док:

Объекты sys.stdin, sys.stdout и sys.stderr инициализируются для файлов объектов, соответствующих стандартному вводу, выводу и потокам ошибок интерпретатора.

Таким образом, следуя этой логике, что -то подобное, кажется, работает. Создайте файл с вашим необходимым вводом:

$ cat sample_stdin.txt
hello
world

Затем перенаправить sys.stdin Чтобы указать на этот файл:

#!/usr/bin/env python
import sys

fh = open('sample_stdin.txt', 'r')
sys.stdin = fh

line1 = raw_input('foo: ')
line2 = raw_input('bar: ')

print line1
print line2

Выход:

$python redirecting_stdin.py
foo: bar: hello
world

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

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

class UserInteraction(object):
    def get_input(self):
        raise NotImplementedError()
    def send_output(self, output):
        raise NotImplementedError()

Тогда вещи, которые должны поговорить с пользователем, могут получить экземпляр вашего класса в качестве аргумента конструктора или функции. Реализация по умолчанию может вызвать фактическую input Функция или что -то еще, но есть версия, используемая для тестирования, которая обеспечивает пример ввода или буферизирует выход, чтобы его можно было проверить.

Это, кстати, именно поэтому я ненавижу Синглтона (который в любом случае не может быть эффективно реализован в Python). Он разрушает вашу способность проверять, создавая экземпляр, который является глобально доступным, который нельзя загрязнять с помощью версии для тестирования.

Мое предложение состоит в том, чтобы рефакторировать ваш код, используя одну из двух структур, которые Python обеспечивает для тестирования единицы: модульный тест (AKA Pyunit) и Doctest.

Это пример с использованием модульный тест:

import unittest

def adder(a, b):
    "Return the sum of two numbers as int"
    return int(a) + int(b)

class TestAdder(unittest.TestCase):
    "Testing adder() with two int"
    def test_adder_int(self):
        self.assertEqual(adder(2,3), 5)

    "Testing adder() with two float"
    def test_adder_float(self):
        self.assertEqual(adder(2.0, 3.0), 5)

    "Testing adder() with two str - lucky case"
    def test_adder_str_lucky(self):
        self.assertEqual(adder('4', '1'), 5)

    "Testing adder() with two str"
    def test_adder_str(self):
        self.assertRaises(ValueError, adder, 'x', 'y')

if __name__ == '__main__':
    unittest.main()

И это пример с использованием Doctest:

# adder.py

def main(a, b):
    """This program calculate the sum of two numbers. 
    It prints an int (see %d in print())

    >>> main(2, 3)
    The sum is 5

    >>> main(3, 2)
    The sum is 5

    >>> main(2.0, 3)
    The sum is 5

    >>> main(2.0, 3.0)
    The sum is 5

    >>> main('2', '3')
    Traceback (most recent call last):
        ...
    TypeError: %d format: a number is required, not str
    """
    c = a + b
    print("The sum is %d" % c)

def _test():
    import doctest, adder
    return doctest.testmod(adder)

if __name__ == '__main__':
    _test()

С Doctest Я сделал еще один пример, используя input () (я полагаю, вы используете Python 3.x):

# adder_ugly.py

def main():
    """This program calculate the sum of two numbers.
    It prints an int (see %d in print())

    >>> main()
    The sum is 5
    """
    a = int(input("Enter an integer: "))
    b = int(input("Enter another integer: "))
    c = a+b
    print("The sum is %d" % c)


def _test():
    import doctest, adder_ugly
    return doctest.testmod(adder_ugly)

if __name__ == '__main__':
    _test()

Я бы запустил каждый из вышеупомянутых примеров с -v вариант:

python adder_ugly.py -v

Для вашей ссылки см.::

http://docs.python.org/py3k/library/unittest.html?highlight=unittest#unittest

а также

http://docs.python.org/py3k/library/doctest.html#module-doctest

Вы можете издеваться над input Функция для предоставления входов из вашей тестовой среды.

Кажется, это может сработать. Это не проверено.

class MockInput( object ):
    def __init__( self, *values ):
        self.values= list(values)
        self.history= []
    def __call__( self, *args, **kw ):
        try:
            response= self.values.pop(0)
            self.history.append( (args, kw, response) )
            return response
        except IndexError:
            raise EOFError()

class TestSomething( unittest.TestCase ):
    def test_when_input_invalid( self ):
        input= MockInput( "this", "and", "that" )
        # some test case based on the input function

Замените Sys.stdin на объект Stringio (или Cstringio).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top