#ruby #unit-testing #testing #rspec #rspec2
#ruby #модульное тестирование #тестирование #rspec #rspec2
Вопрос:
Слишком часто люди пишут тесты, которые не убирают за собой, когда они путаются с состоянием. Часто это не имеет значения, поскольку объекты, как правило, разрушаются и воссоздаются для большинства тестов, но есть несколько неудачных случаев, когда глобальное состояние объектов сохраняется в течение всего тестового запуска, и когда вы запускаете тесты, которые зависят от этого глобального состояния и изменяют его в определенном порядке, они терпят неудачу.
Очевидно, что эти тесты и, возможно, реализации нуждаются в исправлении, но сложно пытаться выяснить, что вызывает сбой, когда тесты, которые влияют друг на друга, могут быть не единственными в полном наборе тестов. Это особенно сложно, когда изначально не ясно, что сбои зависят от порядка и могут прерываться периодически или на одной машине, но не на другой. Например:
rspec test1_spec.rb test2_spec.rb # failures in test2
rspec test2_spec.rb test1_spec.rb # no failures
В RSpec 1 были некоторые опции (—reverse, —loadby) для упорядочения тестовых запусков, но они исчезли в RSpec 2 и в любом случае были лишь минимально полезны при отладке этих проблем.
Я не уверен в порядке, который RSpec 1 или RSpec 2 используют по умолчанию, но один специально разработанный набор тестов, который я использовал в прошлом, случайным образом упорядочивал тесты при каждом запуске, чтобы эти сбои выявлялись быстрее. В выходных данных теста начальное значение, которое использовалось для определения порядка, было напечатано вместе с результатами, так что было легко воспроизвести сбои, даже если вам пришлось проделать некоторую работу, чтобы сузить количество отдельных тестов в наборе, которые их вызывали. Тогда были опции, которые позволяли запускать и останавливать любой заданный тестовый файл в порядке, что позволяло легко выполнять двоичный поиск для поиска проблемных тестов.
Я не нашел никаких подобных утилит в RSpec, поэтому я спрашиваю здесь: какие хорошие способы люди нашли для отладки этих типов зависящих от порядка сбоев тестирования?
Комментарии:
1. Это очень риторический вопрос. Можете ли вы попытаться упростить вопрос, чтобы он был более точным?
2. Для этого и нужен заголовок. И самое последнее предложение. Остальное — прояснить, что я подразумеваю под «зависящим от порядка сбоем теста», поскольку я не нашел других вопросов, связанных с этим, а затем попытаться описать, какое решение я ищу. Если оба из них очевидны для других, читающих вопрос, то я согласен, что другая формулировка слишком риторична, но я не уверен, что это настолько очевидно.
Ответ №1:
Теперь есть --bisect
флаг, который определит минимальный набор тестов для запуска, чтобы воспроизвести сбой. Попробуйте:
$ rspec --bisect=verbose
Также может быть полезно использовать с ним --fail-fast
флаг.
Комментарии:
1. Это потрясающая функция. Отлично решил мою проблему. Спасибо! Должен быть лучший ответ.
Ответ №2:
Я бы не сказал, что у меня есть хороший ответ, и я хотел бы предложить здесь несколько решений получше моего. Тем не менее…
Единственный реальный метод, который у меня есть для отладки этих проблем, — это добавление глобального (через spec_helper) перехвата для печати некоторого аспекта состояния базы данных (мой обычный виновник) до и после каждого теста (при условии, чтобы проверить, волнует меня это или нет). Недавним примером было добавление чего-то подобного в мой spec_helper.rb.
Spec::Runner.configure do |config|
config.before(:each) do
$label_count = Label.count
end
config.after(:each) do
label_diff = Label.count - $label_count
$label_count = Label.count
puts "#{self.class.description} #{description} altered label count by #{label_diff}" if label_diff != 0
end
end
Комментарии:
1. Спасибо за ответ, но мне это не помогает, поскольку в программном обеспечении, которое я тестирую, нет базы данных, а сбои, зависящие от порядка, не настолько предсказуемы, чтобы мне могла помочь инструкция print такого общего характера
![]()
Ответ №3:
У нас есть единственный тест в нашей настройке непрерывной интеграции, который заполняет спецификацию / каталог приложения Rails и запускает каждый из них друг против друга.
Это занимает много времени, но таким образом мы нашли 5 или 6 зависимостей.
Комментарии:
1. У вас есть код для этого под рукой? Это звучит довольно полезно.
Ответ №4:
Вот несколько быстрых грязных скриптов, которые я написал для отладки зависящих от порядка сбоев — https://gist.github.com/biomancer/ddf59bd841dbf0c448f7
Он состоит из 2 частей.
Первый предназначен для многократного запуска rspec suit с разными исходными данными и сброса результатов в rspec_[ok|fail]_[seed].txt
файлы в текущем каталоге для сбора статистики.
Вторая часть перебирает все эти файлы, извлекает имена тестовых групп и анализирует их положение по отношению к затронутому тесту, чтобы сделать предположения о зависимостях и формирует некоторые группы «риска» — безопасные, небезопасные и т.д. Выходные данные скрипта объясняют другие детали и значения группы.
Этот скрипт будет корректно работать только для простых зависимостей и только в том случае, если затронутый тест завершается неудачей для некоторых начальных элементов и проходит для других, но я думаю, что это все же лучше, чем ничего. В моем случае это была сложная зависимость, когда эффект мог быть отменен другим тестом, но этот скрипт помог мне получить указания после многократного запуска его части анализа на разных наборах дампов, особенно только на сбойных (я просто переместил ‘ok’ дампы из текущего каталога).
Ответ №5:
Нашел свой собственный вопрос 4 года спустя, и теперь у rspec есть --order
флаг, который позволяет вам устанавливать случайный порядок, и если вы получаете зависящие от порядка сбои, воспроизводите порядок с помощью --seed 123
где начальное значение распечатывается при каждом запуске спецификации.
https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/command-line/order-new-in-rspec-core-2-8
Ответ №6:
Скорее всего, какое-то состояние сохраняется между тестами, поэтому убедитесь, что ваша база данных и любые другие хранилища данных (включая переменные класса и глобальные переменные) сбрасываются после каждого теста. Может помочь драгоценный камень database_cleaner.
Ответ №7:
Поиск и уничтожение Rspec
предназначен для решения этой проблемы.
Комментарии:
1. Этот драгоценный камень кажется заброшенным и не работает с rspec3