Подтвердить что ты не робот

Ускорение проверки контроллеров rspec: использование до того, как все не удается?

У меня есть простой контрольный тест, содержащий a.o. следующий код:

context "POST :create" do
  before (:each) do
    post :create, :user_id => @user.id,
         :account => { .. some data ... }
  end
  it { response.status.should == 201 }
  it { response.location.should be_present }
end

Теперь я подумал о очень простом способе ускорить этот тест и использовать before(:all) вместо before(:each). В этом случае сообщение будет сделано только один раз.

Итак, я написал:

context "POST :create" do
  before (:all) do
    post :create, :user_id => @user.id,
         :account => { .. some data ... }
  end
  it { response.status.should == 201 }
  it { response.location.should be_present }
end

Но затем я получаю следующие ошибки:

 RuntimeError:
   @routes is nil: make sure you set it in your test setup method.

Это по дизайну? Есть ли способ обойти это?

4b9b3361

Ответ 1

Я задал этот вопрос в списке рассылки rspec и получил следующий ответ от самого @dchelimsky:

Да. rspec-rails обертывает платформу тестирования рельсов, которая не имеет в ней концепции до (: все), поэтому все данные reset перед каждым примером. Даже если бы мы хотели поддержать это в rspec-rails (чего у меня нет), сначала потребуются изменения в рельсах.

Таким образом, выполнение вызовов контроллера невозможно в before(:all), его можно использовать только для настройки ваших переменных DB или экземпляра.

Ответ 2

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

describe PagesController do
  describe "GET 'index'" do
    before(:each) do
      GLOBAL ||= {}
      @response = GLOBAL[Time.now.to_f] || begin
        get :index
        response
      end
    end
    it { @response.should redirect_to(root_path) }
    it { @response.status.should == 301 }
    it { @response.location.should be_present }
  end
end

Рефактор, который вы можете поместить в файл по вашему выбору в spec/support, выглядит следующим образом

RSPEC_GLOBAL = {}

def remember_through_each_test_of_current_scope(variable_name)
  self.instance_variable_set("@#{variable_name}", RSPEC_GLOBAL[variable_name] || begin
    yield
  end)
  RSPEC_GLOBAL[variable_name] ||= self.instance_variable_get("@#{variable_name}")
end

Таким образом, код в тестовом файле становится:

describe PagesController do
  describe "GET 'index'" do
    before(:each) do
      remember_through_each_test_of_current_scope('memoized_response') do
        get :index
        response
      end
    end
    it { @memoized_response.should redirect_to(root_path) }
    it { @memoized_response.status.should == 301 }
    it { @memoized_response.location.should be_present }
  end
end

Надеюсь, что это поможет, и еще раз, используйте с осторожностью

Ответ 3

Я не уверен, что это хорошая идея, но настройка переменной класса с ||= в блоке before(:each) работает:

describe PagesController do
  describe "GET 'index'" do
    before(:each) do
      @@response ||= begin
        get :index
        response
      end
    end
    it { @@response.should redirect_to(root_path) }
    it { @@response.status.should == 301 }
    it { @@response.location.should be_present }
  end
end