Rails 3 Rspec 2: тестирование содержимого для

#ruby-on-rails-3 #haml #rspec2

#ruby-on-rails-3 #haml #rspec2

Вопрос:

Я использую Rails 3.1.1, RSpec 2.7.0 и HAML 3.1.3.

Допустим, у меня есть следующие файлы просмотра:

приложение / представления / макеты /application.html.haml

 !!!
%html
  %head
    %title Test
    = stylesheet_link_tag "application"
    = javascript_include_tag "application"
    = csrf_meta_tags

  %body
    = content_for?(:content) ? yield(:content) : yield
  

приложение / просмотры / макеты /companies.html.haml

 - content_for :content do
  #main
    = yield :main
  #sidebar
    = yield :sidebar

= render :template => 'layouts/application'
  

приложение / просмотры / компании/index.html.haml

 - content_for :main do
  %h1 MainHeader
- content_for :sidebar do
  %h1 SidebarHeader
  

И следующий файл спецификации:

спецификация/просмотры / компании/index_spec.rb

 require 'spec_helper'

describe 'companies/index.html.haml' do

  it 'should show the headers' do
    render
    rendered.should contain('MainHeader')
    rendered.should contain('SidebarHeader')
  end

end
  

Когда я запускаю RSpec, я получаю следующую ошибку:

 1) companies/index.html.haml should show the headers
   Failure/Error: rendered.should contain('MainHeader')
     expected the following element's content to include "MainHeader":
   # ./spec/views/companies/index_spec.rb:7:in `block (2 levels) in <top (required)>'
  

Сначала я подумал, что в RSpec каким-то образом отсутствует content_for для блоков при рендеринге файлов просмотра. Тем не менее, я не смог найти какую-либо проблему, связанную с этим, в репозитории github RSpec, поэтому я не уверен, кто здесь виноват.

Одно (недавнее) решение, которое я нашел, находится наhttp://www.dixis.com/?p=571. Однако, когда я пытаюсь использовать предложенный код

 view.instance_variable_get(:@_content_for)
  

он возвращает ноль.

  • Есть ли способ протестировать content_for в спецификациях представления?
  • Есть ли лучший способ структурировать мои файлы макета таким образом, чтобы я действительно мог их протестировать и при этом достичь того же конечного результата?

Ответ №1:

Используя Rspec 2 с Rails 3, чтобы написать спецификации просмотра для использования content_for, сделайте это:

 view.content_for(:main).should contain('MainHeader')
# instead of contain() I'd recommend using have_tag (webrat)
# or have_selector (capybara)
  

p.s. значение блока content_for(…) по умолчанию является пустой строкой, поэтому, если вы хотите
напишите спецификации, показывающие случаи, в которых content_for(:main) не вызывается, используйте:

 view.content_for(:main).should be_blank
  

Ваша спецификация может быть записана как:

 it "should show the headers" do
  render
  view.content_for(:main).should contain('MainHeader')
  view.content_for(:side_header).should contain('SidebarHeader')
end
  

Таким образом, ваша спецификация точно показывает, что делает ваш view, независимо от какого-либо макета. Я думаю, что для спецификации представления целесообразно протестировать ее изолированно. Всегда ли полезно писать спецификации просмотра? Это открытый вопрос.

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

p.s. если вам нужно указать представление, которое напрямую выводит некоторую разметку и делегирует другую разметку content_for(), вы могли бы сделать это таким образом:

 it "should output 'foo' directly, not as a content_for(:other) block" do
   render
   rendered.should contain('foo')
   view.content_for(:other).should_not contain('foo')
end
it "should pass 'bar' to content_for(:other), and not output 'bar' directly" do
   render
   rendered.should_not contain('bar')
   view.content_for(:other).should contain('bar')
end
  

Это, вероятно, было бы излишним, но я просто хотел показать, как render() заполняет rendered, а view.content_for. «rendered» содержит любые выходные данные, которые view производит напрямую. «view.content_for()» просматривает любой контент, который представление делегировало через content_for().

Комментарии:

1. Я нашел это весьма полезным. Тем не менее, я использую Rspec 2.8, и использование contain приведет к возникновению ошибки undefined method 'contain' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x98acd1c> . Но я просто использую == вместо этого, как в view.content_for(:other).should == 'bar' , и это отлично работает.

2. Спасибо за положительный отзыв! Если вы хотите проверить наличие подстроки и «содержать» не поддерживается для вашей копии rspec, вы можете использовать «включить»: view.content_for(:other). должно включать (‘bar’).

Ответ №2:

Из документов RSpec:

Чтобы предоставить макет для рендеринга, вам нужно явно указать как шаблон, так и макет.

Я обновил спецификацию, и она прошла:

 require 'spec_helper'

describe 'companies/index.html.haml' do

  it 'should show the headers' do
    render :template => 'companies/index', :layout => 'layouts/companies'
    rendered.should contain('MainHeader')
    rendered.should contain('SidebarHeader')
  end

end
  

Ответ №3:

Не беспокойтесь о спецификациях просмотра. Их сложно хорошо написать, и они недостаточно тестируют стек, чтобы его стоило использовать (особенно с учетом сложности написания). Вместо этого используйте Cucumber и в ходе этого проверяйте свои представления.

Как правило, вы также не хотите тестировать content_for конкретно: это реализация, и вместо этого вам следует тестировать поведение. Поэтому просто напишите свои истории с огурцами, чтобы они проверяли желаемый контент.

Если по какой-то странной причине вам действительно нужно протестировать content_for , RSpec имеет синтаксис, который является чем-то вроде body[:content_name] или body.capture :content_name в зависимости от версии (или что-то в этом роде; давно не использовал его). Но внимательно подумайте, есть ли лучший способ проверить то, что вы на самом деле хотите протестировать.

Комментарии:

1. ИМХО, декоратор или презентатор могут использоваться для того, чтобы не писать спецификации просмотра, а также могут ОГРАНИЧИТЬ ваши просмотры или даже сделать их лишенными логики. Но мы не должны просто останавливаться на том, чтобы сказать «не беспокойтесь о спецификациях просмотра» — это слишком общее.

2. @MarnenLaibow-Возможно, косая производительность. Я написал спецификации запроса для тестирования мета-тегов на каждой странице моего приложения, но они настолько медленные, что я неохотно их запускаю. В настоящее время переводим их для просмотра спецификаций, и первоначальные результаты показывают, что они быстрее.

3. @evanrmurphy Они могут быть быстрее, но они не тестируют то, что вам действительно нужно протестировать. (На самом деле, также не запрашиваются спецификации — Cucumber лучше в этом отношении — но они менее ошибочны, чем спецификации просмотра.) Особенно в вашем тестовом коде, сначала заботьтесь о правильности, а затем о производительности.

4. Чтобы усилить мое последнее предложение: неправильный тест, возможно, хуже, чем вообще никакого, потому что он дает вам ложное чувство уверенности в правильности вашего приложения.

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