У нас RSpec годами гонялся в порядке файлов, config.order = :defined. После праздников, пока задач мало, включили :random, и началось: каждый прогон в CI падает от трёх до семи тестов, и каждый раз разные. По отдельности все зелёные. Набор около 2600 примеров, GraphQL API на Rails 8.1 и MySQL. Как искать, кто кому портит состояние, не перебирая руками?
Для этого есть --bisect. Берёте seed упавшего прогона из лога CI, RSpec печатает его в конце строкой «Randomized with seed», и запускаете локально:
bundle exec rspec --seed 41275 --bisect
# ...
# The minimal reproduction command is:
# rspec ./spec/graphql/orders_query_spec.rb[1:3] ./spec/models/user_spec.rb[1:2:1]
Он сам несколько раз режет набор пополам и выдаёт минимальную пару: тест, который портит состояние, и тест, который от этого падает. На 2600 примерах это может занять минут двадцать, но это быстрее, чем гадать. Чаще всего виноваты глобальные вещи: изменённый ENV, настройка класса, которую поменяли в одном тесте и не вернули, Rails.cache, глобальные переменные.
Ещё классика: тест меняет настройку прямым присваиванием, например PaymentGateway.timeout = 1, и не возвращает обратно. Такое лучше делать через allow(PaymentGateway).to receive(:timeout).and_return(1) или stub_const, RSpec сам всё откатит после примера. Ну или хотя бы around с ensure.
и проверьте, что в test.rb у кеша стоит :null_store. его иногда меняют ради одного теста, и кеш начинает переезжать из примера в пример
--bisect за 25 минут нашёл первую пару: тест на мутацию заказа ставил ENV["FEATURE_SPLIT_PAYMENTS"] = "1" и не убирал, а запрос на список заказов дальше считал, что фича включена. Остальные шесть оказались той же природы: ещё два ENV, три падения из-за memory_store, который кто-то включил в test.rb ради одного теста, и один класс с настройкой через cattr_accessor. За два вечера починили, 20 прогонов подряд зелёные, :random оставили.