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