Ответ 1
Я могу подумать о двух решениях вашей проблемы.
Но сначала нам нужно выяснить, где корень проблемы.
Корень проблемы
Начните с строки из вашего кода:
Rake::Task[task].enhance ['guard_dangerous_tasks']
Сравнивая его с исходным кодом Rake::Task
# File rake/task.rb, line 96
def enhance(deps=nil, &block)
@prerequisites |= deps if deps
@actions << block if block_given?
self
end
вы можете видеть, что guard_dangerous_tasks
следует добавить в массив @prerequisites
. Его можно легко проверить:
p Rake::Task['db:reset'].prerequisites # => ["environment", "load_config", "guard_dangerous_tasks"]
Продолжая с вами исходный код.
Вы используете invoke
для выполнения задач. Если мы уделяем пристальное внимание документации invoke
', в ней говорится:
Вызвать задачу, если она нужна.
Как только задача будет выполнена, ее нельзя было бы вызвать снова (если мы не будем ее повторно использовать).
Но почему это должно быть проблемой? У нас разные задачи, не так ли? Но на самом деле мы этого не делаем!
Мы запускаем guard_dangerous_tasks
перед всеми задачами в нашем массиве задач! И он выполняется только один раз.
Решение № 1 не самое лучшее
Как только мы узнаем, где наша проблема, мы можем думать об одном (не лучшее решение).
После каждой итерации повторите функцию guard_dangerous_tasks
:
dangerous_task = Rake::Task['guard_dangerous_tasks']
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
dangerous_task.reenable
end
Решение №2 guard_dangerous_tasks
не является обязательным условием
Мы получаем лучшее решение нашей проблемы, если осознаем, что guard_dangerous_tasks
не должно быть обязательным условием! Предпосылки должны быть подготовлены и исполняться только один раз. Но мы никогда не должны ослеплять наши глаза от опасностей!
Вот почему мы должны расширять с помощью guard_dangerous_tasks
как действие, которое будет выполняться каждый раз при выполнении родительской задачи.
Согласно исходному коду Rake::Task
(см. выше), мы должны передать нашу логику в блоке, если хотим, чтобы он был добавлен как действие.
%w[ db:setup db:reset ].each do |task|
Rake::Task[task].enhance do
Rake::Task['guard_dangerous_tasks'].execute
end
end
Мы можем оставить наш тест без изменений и передать:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
Но оставить invoke
- это билет на новые проблемы. Лучше заменить на execute
:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
end
Будьте осторожны с invoke
!
Мы сказали выше, что использование invoke
- это билет для новых проблем. Какие проблемы?
Попробуйте проверить наш код для сред test
и production
. Если мы завершим наши тесты в этом цикле:
['production','test'].each do |env_name|
env = ActiveSupport::StringInquirer.new(env_name)
allow(Rails).to receive(:env).and_return(env)
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
наш тест завершится неудачно с оригинальной причиной. Вы можете легко исправить это, заменив строку
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
с
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
Так в чем же причина? Вероятно, вы уже догадались.
В неудачном тесте мы дважды вызывали те же две задачи. В первый раз они были казнены. Во второй раз они должны быть повторно подключены перед выполнением вызова. Когда мы используем execute
, действие автоматически включается.
Примечание Здесь вы можете найти рабочий пример этого проекта: https://github.com/dimakura/stackoverflow-projects/tree/master/31821220-testing-rake