Как проверить, если __name__ == "__main__" с передачей аргументов командной строки? - PullRequest
1 голос
/ 13 февраля 2020

Привет, я хочу проверить мой исполняемый модуль main.py. В этом модуле есть функция main(), которая принимает два аргумента:

# main.py

def main(population_size: int, number_of_iterations: int):
    ...

В нижней части этого модуля находится logi c, который принимает аргументы командной строки и выполняет main функцию:

# main.py

if __name__ == "__main__":
    # create parser and handle arguments
    PARSER = argparse.ArgumentParser()
    PARSER.add_argument("--populationSize",
                        type=int,
                        default=-1,
                        help="Number of individuals in one iteration")
    PARSER.add_argument("--numberOfIterations",
                        type=int,
                        default=-1,
                        help="Number of iterations in one run")
    # parse the arguments
    ARGS = PARSER.parse_args()

    main(ARGS.populationSize, ARGS.numberOfIterations)

Я хочу проверить передачу аргументов командной строки. Мой тестовый метод, который не работает:

# test_main.py

@staticmethod
@mock.patch("argparse.ArgumentParser.parse_args")
@mock.patch("main.main")
def test_passing_arguments(mock_main, mock_argparse):
    """Test passing arguments."""
    mock_argparse.return_value = argparse.Namespace(
         populationSize=4, numberOfIterations=3)
    imp.load_source("__main__", "main.py")

    mock_main.assert_called_with(4, 3)

Я получаю ошибку, что mock_main не вызывается. Я не знаю почему. Насколько я понимаю, я высмеял функцию main из модуля main. Макет функции main необходим, потому что это отнимает много времени, и здесь я хочу проверить только то, что параметры передаются правильно.

С этого поста Я использовал способ пересмешивания модуля argparse .

Ответы [ 2 ]

6 голосов
/ 13 февраля 2020

Как и весь код, который вы хотите протестировать, оберните его в функцию.

def parse_my_args(argv=None):
    PARSER = argparse.ArgumentParser()
    PARSER.add_argument("--populationSize",
                    type=int,
                    default=-1,
                    help="Number of individuals in one iteration")
    PARSER.add_argument("--numberOfIterations",
                    type=int,
                    default=-1,
                    help="Number of iterations in one run")
    # parse the arguments
    return PARSER.parse_args(argv)


if __name__ == '__main__':
    args = parse_my_args()
    main(args.populationSize, args.numberOfIterations)

ArgumentParser.parse_args обрабатывает любой список строк, которые вы передаете. Когда вы передаете None, вместо него используется sys.argv[1:].

Теперь вы можете проверить parse_my_args, просто передав любой требуемый список аргументов.

# test_main.py

@staticmethod
def test_passing_arguments():
    """Test passing arguments."""
    args = parse_my_args(["--populationSize", "4", "--numberOfIterations", "3"])
    assert args.populationSize == 4
    assert args.numberOfIterations == 3

Если Вы также хотите убедиться, что правильные аргументы переданы main, обернуть , что в функцию и использовать mock, как вы делали выше.

def entry_point(argv=None):
    args = parse_my_args(argv)
    main(args.populationSize, args.numberOfIterations)

if __name__ == '__main__':
    entry_point()

и

@staticmethod
@mock.patch("main.main")
def test_passing_arguments(mock_main):
    """Test passing arguments."""
    entry_point(["--populationSize", "4", "--numberOfIterations", "3"])
    mock_main.assert_called_with(4, 3)
3 голосов
/ 13 февраля 2020

Я обычно пишу свой код командной строки следующим образом. Сначала переименуйте существующую функцию main во что-то другое, например run() (или что-то еще):

def run(population_size: int, number_of_iterations: int):
    ...

Затем напишите функцию main(), которая реализует интерфейс командной строки и анализ аргументов. Пусть он принимает argv в качестве необязательного аргумента, который отлично подходит для тестирования:

def main(argv=None):
    parser = argparse.ArgumentParser()
    ...
    args = parser.parse_args(argv)
    run(args.popuplation_size, args.number_of_iterations)

Затем в теле модуля просто введите:

if __name__ == '__main__':
    sys.exit(main())

Теперь у вас есть правильный main() функция, которую вы можете легко протестировать, не беспокоясь о контексте, в котором она была вызвана, и не выполняя какие-либо странные мартышки, например:

main(['--populationSize', '4', '--numberOfIterations', '3'])
...