Тестирование пользовательского ввода — Python

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

Во-первых, вот фрагмент моего основного файла кода, который я хочу протестировать:

if __name__ == '__main__':

    n = int(input())
    m = int(input())

    grid = []

    for _ in range(n):
        grid.append(list(map(str, input().rstrip().split())))

    calculate(grid)

Когда я запускаю свой код, я ввожу «n», затем «m», затем создается сетка в соответствии с вводом пользователя (каждая строка в новой строке..), и выполняется функция, которая вычисляет что-то в сетке и функция возвращает результат. Все это отлично работает, но теперь мне нужно создать для него пару тестовых случаев (которые проверяют разные входные данные на ожидаемые результаты).

Сначала я попробовал это: (в отдельном файле .py)

from unittest import mock
from unittest import TestCase
import main_file

class DictCreateTests(TestCase):
    @mock.patch('main_file.input', create=True)
    def testdictCreateSimple(self, mocked_input):
        mocked_input.side_effect = ['2', '2', 'R G B\nR G B'] #this is the input I need for my color grid
        self.assertEqual(calculate(grid), 2)

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

Затем я исследовал еще несколько вариантов и попробовал этот вариант, который мне ближе всего:

import unittest
import os

class Test1(unittest.TestCase):

    def test_case1(self):
        input = "2\n2\nR G B\nR G B"
        expected_output = '2'
        with os.popen("echo " + input + "' | python main_file.py") as o:
            output = o.read()
        output = output.strip() # Remove leading spaces and LFs
        self.assertEqual(output, expected_output)

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

К сожалению, несмотря на то, что он прошел тест, я обнаружил, что в результате он всегда принимает первую букву/цифру ввода, когда сравнивает его с ожидаемым результатом. Итак, я думаю, что это как-то связано с несколькими значениями, которые мне нужно ввести. Я попытался разделить их на разные входы (вход1 + ввод2+ ввод3), но это все равно не сработало.

Я был бы очень признателен, если бы кто-нибудь мог дать мне несколько советов о том, как это сделать! Заранее спасибо!

Вы проверили этот stackoverflow.com/questions/21046717/…?

Umair Mohammad 28.05.2019 12:43
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
940
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я предлагаю рефакторить код, чтобы вы могли протестировать функцию:

def create_grid_and_calculate(n, m):
    grid = []

    for _ in range(n):
        grid.append(list(map(str, input().rstrip().split())))

    return calculate(grid)


if __name__ == '__main__':

    n = int(input())
    m = int(input())

    create_grid_and_calculate(n, m)

потом

import unittest
import os
from main_file import create_grid_and_calculate

class Test1(unittest.TestCase):

    def test_case1(self):
        expected_output = '2'
        self.assertEqual(create_grid_and_calculate(2, 2), expected_output)
        self.assertEqual(create_grid_and_calculate(int("R G B"), int("R G B")), expected_output)

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

Вы также можете заменить свои входные данные аргументами, переданными в командной строке, проанализированными специальным модулем (например, argparse является стандартным), чтобы лучше контролировать свои входные данные.

import argparse

def create_grid_and_calculate(n, m):
    ...


def main(argv: list = None):
    parser = argparse.ArgumentParser(description = "My script...")
    parser.add_argument(
        "-m",
        dest = "m",
        action = "store",
        type=int,
        help = "parameter m",
    )
    parser.add_argument(
        "-n",
        dest = "n",
        action = "store",
        type=int,
        help = "parameter n",
    )
    args = parser.parse_args(argv or [])

    create_grid_and_calculate(args.n, args.m)

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv[1:]))

Таким образом, вы также можете протестировать основную функцию с различными входными данными (int, strings...).

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


Обновлено: Чтобы определить свою сетку, вам не нужно вводить размеры (n и m) и вам не нужно вводить каждую строку отдельно. Выберите 1 разделитель для строки (здесь запятая), другой для столбцов (здесь пробел), и вы получите:

import argparse

def main(argv: list = None):
    parser = argparse.ArgumentParser(description = "My script...")
    parser.add_argument(
        "-g",
        "--grid",
        dest = "grid",
        action = "store",
        type=str,
        help = "The grid, defined as 'x11 x12 ... x1n, x21 x22 ... x2n, ...'",
    )
    args = parser.parse_args(argv or [])

    # first we split on comma, then on space
    grid = [x.split() for x in args.grid.split(',')]
    print(grid)
    calculate(grid)

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv[1:]))

и вы запускаете это так: main_file.py -g '1 2 3, 4 5 6, 7 8 9'

[['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']]

Чтобы получить список списка int, используйте:

grid = [[int(val) for val in row.split()] for row in args.grid.split(',')]

или, возможно, более четко:

grid = []
for row in args.grid.split(','):
    grid.append([])
    for val in row.split():
        grid[-1].append(int(val))

Большое спасибо за ответ! Мне очень нравится рефакторинг основного кода, но у меня все же есть небольшой вопрос по тестовому коду - в self.assertEqual(create_grid_and_calculate(int("RGB"), int("RGB")), expect_output) вы пытаются поставить в качестве параметров то, что пользователь вводит в функцию в соответствии с параметрами n и m (строки и столбцы). И я думаю, что это может быть причиной того, что тестовый пример работает в цикле. Я понимаю вашу логику — я только не могу понять, как включить этот «второй ввод» — тот, где вы помещаете «R G B» в сетку, чтобы он мог в конечном итоге сравнить их.

Gergan Zhekov 28.05.2019 13:15

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