Шаблоны для доступа к предметной иерархии в RSpec - PullRequest
10 голосов
/ 23 сентября 2011

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

Прямо сейчас мой код выглядит примерно так:

context 'with a result which is a Hash' do
  before do
    @result = get_result()
  end
  subject { @result }
  it { should be_a Hash }
  context 'with an Array' do
    before do
      @array_elem = @result[special_key]
    end
    subject { @array_elem }
    it { should be_an Array }
    context 'that contains a Hash' do
      before do
        @nested_hash = ...
      end
      subject { @nested_hash }
      ...
    end
  end
end

Вместо этого я бы предпочел написать что-то вроде:

context 'with a result which is a Hash' do
  subject { get_result }
  it { should be_a Hash }
  context 'with an Array' do
    subject { parent_subject[special_key] }
    it { should be_an Array }
    context 'that contains a Hash' do
      subject { do_something_with(parent_subject) }
      ...
    end
  end
end

Как можно расширить RSpec с помощью этого типа автоматического управления субъектной иерархией?

Ответы [ 4 ]

10 голосов
/ 05 июня 2013

Я искал то же самое и хотел использовать subject, чтобы я мог использовать its, поэтому я реализовал это так:

describe "#results" do
  let(:results) { Class.results }

  context "at the top level" do
    subject { results }

    it { should be_a Hash }
    its(['featuredDate']) { should == expected_date }
    its(['childItems']) { should be_a Array }
  end

  context "the first child item" do
    subject { results['childItems'][0] }

    it { should be_a Hash }
    its(['title']) { should == 'Title' }
    its(['body']) { should == 'Body' }
  end

  context "the programme info for the first child item" do
    subject { results['childItems'][0]['programme'] }

    it { should be_a Hash }
    its(['title']) { should == 'title' }
    its(['description']) { should == 'description' }
  end
end
6 голосов
/ 12 ноября 2013

Я нашел этот вопрос, пытаясь сделать что-то подобное. Мое решение можно найти на https://gist.github.com/asmand/d1ccbcd01789353c01c3

Что нужно сделать, чтобы проверить расчет рабочего времени на неделю на Рождество, то есть на данную неделю только понедельник и пятница - рабочие дни, остальные - выходные.

Решение основано на названных предметах. * 1007 Т.е. *

describe WeeklyFlexCalculator, "during Christmas week" do

  subject(:calculation) { WeeklyFlexCalculator.new(params).calculate }

  context "with no work performed" do
    it { should have(1).item }

    context "the week calculated" do
      subject(:workweek) {calculation[0]}

      its([:weekTarget]) { should eq 15.0 }
      its([:weekEffort]) { should eq 0.0 }

      context "the work efforts" do
        subject(:efforts) {workweek[:efforts]}

        it { should have(2).items }

        context "the first work effort" do
          subject(:effort) {efforts[0]}

          its([:target]) {should eq 7.5}
          its([:diff]) {should eq -7.5}
          its([:effort]) {should eq 0.0}
        end
      end
    end
  end
end

Большая часть кода оставлена ​​для краткости, но полный пример можно найти в связанной сущности.

2 голосов
/ 02 октября 2011

В этом типе иерархической структуры я бы фактически отказался от использования subject и сделал бы предмет явным.Хотя это может привести к некоторому увеличению объема печати (если у вас много тестов), это также более понятно.Вложенные subject -общения могут также вводить в заблуждение то, что на самом деле тестируется, если вы на три уровня ниже.

  context 'with a result which is a Hash' do
    before do
      @result = get_result()
    end
    it { @result.should be_a Hash }
    context 'special_key' do
      before do
        @array_elem = @result[special_key]
      end
      it { @array_elem.should be_an Array }
      context 'that contains a Hash' do
        before do
          @nested_hash = ...
        end
        it { @nested_hash.should be_a Hash }
        ...
      end
    end
  end

Но это может быть вопросом вкуса.Надеюсь, это поможет.

2 голосов
/ 23 сентября 2011

Я предполагаю, что эта функциональность не встроена в Rspec, потому что она будет стимулировать более сложные спецификации и код.В соответствии с передовой практикой ООП классы должны нести единоличную ответственность.В результате ваши спецификации должны быть краткими и простыми для понимания.Для краткости и читабельности у спецификации должен быть только один тип предмета.Могут быть вариации на эту тему, но в конечном итоге все они должны основываться на классе / объекте, который вы описываете.

На вашем месте я бы сделал шаг назад и действительно спросил бы себя, что ядействительно пытаюсь сделать.Это похоже на запах кода, если вы обнаружите, что у вас несколько предметов разного класса в одной спецификации.Это либо проблема с тем, как вы используете Rspec, либо ваш класс делает слишком много.Следует также отметить, что вы должны тестировать поведение ваших объектов, а не детали того, что они делают внутри.Кого волнует, является ли что-то массивом или хэшем, если он ведет себя так, как должен?

Удаляет ... Должен ли ваш дочерний предмет действительно быть отдельным классом со своей спецификацией?Вы перестали тестировать реализацию, а не поведение?

...