Шаблоны для доступа к иерархии объектов в RSpec
При использовании 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 с помощью этого типа автоматического управления иерархией объектов?
Ответы
Ответ 1
В этом типе иерархической структуры я бы фактически отказался от использования 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
Я искал одно и то же, и хотел использовать 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
Ответ 3
Я нашел этот вопрос, пытаясь сделать что-то подобное. Мое решение можно найти на https://gist.github.com/asmand/d1ccbcd01789353c01c3
То, что он делает, - это проверить расчет времени работы в течение недели за Рождество, то есть на данной неделе, только понедельник и пятница - рабочие дни, остальные - праздники.
Решение основано на названных предметах. То есть.
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
Много кода для краткости осталось, но полный пример можно найти в связанном сущности.
Ответ 4
Я предполагаю, что эта функциональность не встроена в Rspec, потому что она будет стимулировать более сложные спецификации и код. Согласно передовым методам ООП, классы должны нести единую ответственность. В результате ваши спецификации должны быть краткими и понятными. Для краткости и удобочитаемости спецификация должна иметь только один тип предмета. Могут быть вариации на эту тему, но в конечном итоге все они должны основываться на классе/объекте, который вы описываете.
Если бы я был вами, я бы сделал шаг назад и действительно спросил себя, что я действительно пытаюсь сделать. Это похоже на запах кода, если вы обнаруживаете, что у вас есть несколько предметов разного класса в одной и той же спецификации. Это либо проблема с тем, как вы используете Rspec, либо ваш класс делает слишком много. Другое дело, что вы должны тестировать поведение своих объектов, а не детали того, что они делают внутри. Кто заботится, если что-то является массивом или хешем, если он ведет себя так, как должен?
Возьмите aways... Если ваш ребенок действительно будет отдельным классом со своей собственной спецификацией? Вы испытываете тестирование, а не поведение?