@thewizardplusplus

Как тестировать сложные функции с побочными эффектами?

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

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

Не тестировать её вовсе, так как вся функциональность протестирована уже в маленьких функциях? Так нельзя - а вдруг я написал эту большую функцию неверно (вызвал маленькие не в той последовательности или забыл что-то). Тесты нужны, вопрос лишь, какие.

Так же интересует, как тестировать функции с побочными эффектами. Да, я знаю про внедрение зависимостей, но получается его нужно использовать всегда-всегда, чтобы тестирование было возможным? Не усложнит ли это код, не сделает ли его более запутанным и нечитаемым?

* * *

Ну и чтобы было понятнее, приведу конкретный пример. Предположим, нам нужно обработать аргументы командной строки, передаваемые в программу. А именно:

  • при наличии аргумента -v вывести версию;
  • провалидировать значения остальных аргументов (скажем, что они попадают в определённые лимиты или входят в указанный список).


Мы написали функцию для этого (некий абстрактный псевдоязык):

function ProcessArgs(args) {
    let parsed_args = ParseArgs(args)
    ShowVersionIfNeed(parsed_args)
    ValidateValues(parsed_args)

    return parsed_args
}


Причём, функции ParseArgs(), ShowVersionIfNeed() и ValidateValues() тоже реализованы и, что важно, покрыты тестами.

Вопрос - как нужно тестировать функцию ProcessArgs()? Перебирать все возможные варианты для всех внутренних функций? То есть по сути перебирать все написанные для них тесты во всех возможных сочетаниях? Или как?

И второй вопрос. Вот функция ShowVersionIfNeed():

function ShowVersionIfNeed(args) {
    if args.contains('-v') {
        sys.Exit(sys.ExitSuccess)
    }
}


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

Добавить внедрение зависимостей?

function ProcessArgs(args, exiter) {
    let parsed_args = ParseArgs(args)
    ShowVersionIfNeed(parsed_args, exiter)
    ValidateValues(parsed_args)

    return parsed_args
}

function ShowVersionIfNeed(args, exiter) {
    if args.contains('-v') {
        exiter.Exit(sys.ExitSuccess)
    }
}


Но это же усложняет и запутывает код. Или нет и так делать нормально и правильно?
  • Вопрос задан
  • 439 просмотров
Решения вопроса 1
k12th
@k12th
console.log(`You're pulling my leg, right?`);
Как тестировать эту общую функцию? Ведь она покрывает все возможные варианты исполнения кода в этих маленьких функциях.

Мне кажется, тут надо смотреть с точки зрения BDD, то есть проверять не какие-то синтетические тесты, а проверять, что программа корректно себя ведет в тех или иных ситуациях.

Как видим, в ней есть прерывание программы. Как такое тестировать?

Замокать sys.Exit, если язык позволяет, ну или использовать DI, да. DI же не только для тестов нужен, а еще для уменьшения связанности.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы