Рефакторинг спецификации Rspec - PullRequest
3 голосов
/ 29 апреля 2010

Я пытаюсь очистить свои спецификации, так как они становятся чрезвычайно повторяющимися.

У меня есть следующая спецификация

  describe "Countries API" do 
    it "should render a country list" do
      co1 = Factory(:country)
      co2 = Factory(:country)
      result = invoke :GetCountryList, "empty_auth"

      result.should be_an_instance_of(Api::GetCountryListReply)
      result.status.should be_an_instance_of(Api::SoapStatus)
      result.status.code.should eql 0
      result.status.errors.should be_an_instance_of Array
      result.status.errors.length.should eql 0
      result.country_list.should be_an_instance_of Array
      result.country_list.first.should be_an_instance_of(Api::Country)
      result.country_list.should have(2).items
    end
    it_should_behave_like "All Web Services"
    it "should render a non-zero status for an invalid request"
  end

Блок кода, который проверяет статус, появится во всех моих спецификациях для 50-60 API. Моей первой мыслью было перенести это в метод, и этот рефакторинг, безусловно, делает вещи намного более сухими: -

def status_should_be_valid(status)
    status.should be_an_instance_of(Api::SoapStatus)
    status.code.should eql 0
    status.errors.should be_an_instance_of Array
    status.errors.length.should eql 0
end

describe "Countries API" do 
    it "should render a country list" do
      co1 = Factory(:country)
      co2 = Factory(:country)
      result = invoke :GetCountryList, "empty_auth"

      result.should be_an_instance_of(Api::GetCountryListReply)
      status_should_be_valid(result.status)
      result.country_list.should be_an_instance_of Array
      result.country_list.first.should be_an_instance_of(Api::Country)
      result.country_list.should have(2).items
    end
  end

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

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

  co1 = Factory(:country)
  co2 = Factory(:country)
  result = invoke :GetCountryList, "empty_auth"

1 Ответ

3 голосов
/ 29 апреля 2010

Вот одна из опций, использующая новую функцию «субъекта» RSpec. Обратите внимание, что при этом блок before :all будет запущен дважды, по одному разу для каждого вложенного блока «описать». Если это окажется слишком медленным, вы можете сгладить ситуацию за счет невозможности использовать «субъектный» синтаксис для общих примеров статуса (поскольку субъект применяется ко всему блоку описания, в котором он используется).

shared_examples_for "valid status" do
  it { should be_an_instance_of(Api::SoapStatus) }
  its(:code) { should eql(0) }
  its(:errors) { should be_an_instance_of(Array) }
  its(:errors) { should be_empty }
end

describe "Countries API" do
  before :all do
    co1 = Factory(:country)
    co2 = Factory(:country)
    @result = invoke :GetCountryList, "empty_auth"
  end

  subject { @result }
  it { should be_an_instance_of(Api::GetCountryListReply) }
  its(:country_list) { should be_an_instance_of (Array) }
  it "should have countries in the country list" do
    @result.country_list.each {|c| c.should be_an_instance_of(Api::Country)}
  end
  its(:country_list) { should have(2).items }

  describe "result status" do
    subject { @result.status }
    it_should_behave_like "valid status"
  end
end
...