2014-02-14 5 views
3

tl; dr - Я хочу написать функцию Python unittest, которая удаляет файл, запускает тест и восстанавливает файл. Это вызывает условия гонки, так как unittest выполняет несколько тестов параллельно, а удаление и создание файла для одного теста противоречит другим тестам, которые происходят одновременно.Выполнение модульного теста Python, который никогда не работает параллельно

Long Конкретный пример:

У меня есть модуль Python с именем converter.py и это связано тесты в test_converter.py. Если есть файл с именем config_custom.csv в том же каталоге, что и converter.py, тогда будет использована настраиваемая конфигурация. Если нет настраиваемого конфигурационного файла CSV, тогда есть конфигурация по умолчанию, встроенная в converter.py.

Я написал модульный тест, используя unittest из стандартной библиотеки Python 2.7 для проверки этого поведения. Единичный тест в setUp() переименует config_custom.csv в wrong_name.csv, затем он будет запускать тесты (надеюсь, используя конфигурацию по умолчанию), а затем в tearDown() он переименует файл так, как он должен быть.

Задача: Испытания модуля Python выполняются параллельно, и я получил ужасные условия гонки. Файл config_custom.csv будет переименован в середине других модульных тестов недетерминированным способом. Это приведет к по меньшей мере одной ошибке или ошибке примерно в 90% случаев, когда я запустил весь тестовый пакет.

Идеальное решение было бы сказать unittest: НЕ ПРОДАЕТСЯ этот тест параллельно с другими тестами, этот тест является особым и требует полной изоляции.

Моей совместной работой является добавление необязательного аргумента функции, которая ищет файлы конфигурации. Аргумент передается только тестовым набором. Он игнорирует файл конфигурации, не удаляя его. Фактически удаление тестового файла более изящно, вот что я действительно хочу проверить.

ответ

1

Во-первых, большинство испытательных платформ, поддерживающих параллелизм, также поддерживают методы серийных важных испытаний. Например Python Fabric поддерживает последовательную аннотацию (когда вы запустите тест в качестве параллельного пакета с флагом командной строки -P):

from fabric.api import * 

def runs_in_parallel(): 
    pass 

@serial 
def runs_serially(): 
    pass 

Однако, я не думаю, что вы должны сделать это, я думаю, вы должны остановиться и презирать на мгновение. Следуя Single Responsibility Principal, это то, что я бы сказал, происходит.

Вы находитесь в ситуации, когда тест по существу определил условие вашего кода, оно выделяет небольшое пятно сплоченности для вас.

Теперь вы должны понимать, что «converter.py» имеет слишком много обязанностей, он не только выполняет сложную операцию под капотом, но и несет ответственность за управление собственным созданием и настройкой таким образом, чтобы вероятно, слишком много для этого.

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

1

Проблема в том, что имя config_custom.csv должно быть настраиваемым параметром.Затем каждый тест может просто искать config_custom_<nonce>.csv, и любое количество тестов может выполняться параллельно.

Очистка всего комплекта может просто очистить config_custom_*.csv, так как мы не будем нуждаться ни в одном из них в этот момент.

0

Лучшая стратегия тестирования заключается в том, чтобы убедиться, что вы тестируете на непересекающихся наборах данных. Это обойдет любые условия гонки и упростит код. Я бы также высмеял open или __enter__/__exit__, если вы используете контекстный менеджер. Это позволит вам подделать событие, чтобы файл не существовал.

Смежные вопросы