Тесты RSpec не срабатывают, когда работают dev и prod - ошибка RSpec, виртуальный атрибут или проблема с формой? - PullRequest
0 голосов
/ 17 декабря 2011

Я пишу базовое приложение, используя RoR, и я тестирую с RSPec (и Factory Girl).Мое приложение работает как на Dev, так и на Prod, но я не могу пройти все мои тесты RSpec.Я подозреваю, что это какая-то особенность RSpec или что она связана с массовым назначением (виртуальных атрибутов).Я дам как можно больше информации, но, пожалуйста, дайте мне знать, если я опущу что-нибудь подходящее.(Некоторое время я занимался троллингом, но это мой первый пост, поэтому я уверен, что кое-что забуду.)

РЕДАКТИРОВАТЬ: я должен сказать, что все тесты проходили до того, как я внедрил виртуальныйатрибут (который я сделал, чтобы использовать автозаполнение JQuery-UI в этом поле имени пользователя).

Вот мои настройки:

Rails 3.1.1
Ruby 1.9.2p290
RSpec 2.6.4 (это то, что я получаю rspec -v из командной строки, ноGemfile говорит 2.6.1 - я не уверен, что с этим)
Factory Girl 1.0 (gem factory_girl_rails) Сайт разработчика - SQLite3
Prod размещен на heroku (postgres)

Во-первых, здесьявляется ошибочным результатом RSpec (на item_shares_controller.rb) - я включаю только один сбой для экономии места.Проблема одинакова при каждой неудаче (из 10).Я сосредоточен на этом, потому что я могу создать новый item_share на dev / prod, так что это обязательно должно пройти тест:

  5) ItemSharesController POST 'create' success should create an item_share
     Failure/Error: post :create, @attr
     ActiveRecord::RecordInvalid:
       Validation failed: Receiver is required. Please enter a valid username.
     # ./app/controllers/item_shares_controller.rb:25:in `create'
     # ./spec/controllers/item_shares_controller_spec.rb:71:in `block (5 levels) in <top (required)>'
     # ./spec/controllers/item_shares_controller_spec.rb:70:in `block (4 levels) in <top (required)>'

ПРИМЕЧАНИЕ: я отметил соответствующие строки вкод ниже (25, 70, 71).

Таким образом, проблема заключается в том, что контроллер не получает атрибут: receive_username из моей формы.Вот форма:

<%= form_for(@item_share) do |f| %>
  <%= render 'shared/error_messages', :object => f.object %>
  <div class="field">
    <%= fields_for :item do |i| %>
      <%= i.label "What?" %></br>
      <%= i.text_field :link_url, { :placeholder => 'http://...' }  %>
    <% end %>
  </div>
  <div class="field">
      <%= f.label "With who?" %></br>
      <%= f.text_field :receiver_username, :placeholder => 'JohnDoe123', data: { autocomplete_source: autocomplete_users_path } %>
  </div>
  <div class = "field">
    <%= f.label "Why?" %></br>
    <%= f.text_area :note, { :placeholder => '(Optional) In 140 characters or fewer, add a note.' }  %>
  </div>
  <div class="actions">
    <%= f.submit "Submit" %>
  </div>
<% end %>

Обратите внимание, что соответствующее поле здесь - это поле: receiver_username, которое будет передавать имя пользователя контроллеру.Это поле является виртуальным атрибутом модели item_share (item_share.rb ниже), и вы можете видеть, что я использую функцию автозаполнения jQuery-ui для этого поля.

Вот контроллер: метод create (I'mдля краткости не помещаем здесь весь контроллер):

#item_shares_controller.rb

def create

  @item = Item.new()
  @item.link_url = params[:item] ? params[:item][:link_url] : ""


  params_username = params[:item_share] ? params[:item_share][:receiver_username] : ""

  @receiver = User.find(:first, :conditions => [ "lower(username) = ?", params_username.to_s.downcase ] )

  @giver = current_user

  @note = params[:item_share] ? params[:item_share][:note] : ""

  @item_share = current_user.item_shares_given.build(:item => @item, :receiver => @receiver, :note => @note)

  # The next line is 25 as called out in the failure
  if  @item_share.save!()
    if @receiver.send_email? && !(@receiver == @giver)
      UserMailer.new_share_notification(@receiver, @giver, @item_share).deliver
    end
    flash[:success] = "You shared a link!"
    redirect_to root_path
  else
    @list_items = []
    flash.now[:error] = "The link was not shared."
    render 'pages/home'
  end

end

Вот модель item_share (где я определил receive_username как виртуальный атрибут, и вы можете увидеть методы setter / getter в нижней части модели):

# item_share.rb

class ItemShare < ActiveRecord::Base
  attr_accessible :note, :item, :receiver, :share_source_user_id, :share_target_user_id, :item_id

  # ItemShares relation to Users
  belongs_to :giver, :class_name => "User",
    :foreign_key => "share_source_user_id"
  belongs_to :receiver, :class_name => "User",
    :foreign_key => "share_target_user_id"

  # ItemShares relation to Items
  belongs_to :item

  default_scope :order => 'item_shares.created_at DESC'

  validates :note, :length => { :within => 0..140, :message => "must be 140 characters or fewer." } 
  validates_associated :item, :message => "is not valid. Please enter a valid URL."
  validates :receiver, :presence => {:message => "is required. Please enter a valid username." }
  validates :share_source_user_id, :presence => true

  def receiver_username
    receiver.try(:username)
  end

  def receiver_username=(username)
    self.receiver = Receiver.find_by_username(username) if username.present?
  end

end

А вот некоторые из моего файла item_shares_controller_spec.rb (я пытался урезать его, чтобы показать, что уместно, но я не включаю все это, опять же для краткости):

# item_shares_controller_spec.rb

require 'spec_helper'

describe ItemSharesController do
  render_views

  describe "POST 'create'" do

    before(:each) do
      @user = test_sign_in(Factory(:user))
    end

    describe "success" do

      before(:each) do
        @receiver = Factory(:user, :name => Factory.next(:name),
                            :username => Factory.next(:username),
                            :email => Factory.next(:email))
        @item = Factory(:item)
        @attr = {:item =>{:link_url=>@item.link_url}, :item_share =>{:receiver_username=>@receiver.username}, :item_share=>{:note=>"Note"}}
      end

      it "should create an item_share" do
        # Next lines are 70 and 71 as called out in the failure
        lambda do
          post :create, @attr
        end.should change(ItemShare, :count).by(1)
      end
    end
  end
end

А вот мой файл factories.rb (я не думаю, что это связано с проблемой, но я мог бы также включить его):

#factories.rb

# By using the symbol ':user', we get Factory Girl to simulate the User model.
Factory.define :user do |user|
  user.name                  "Michael Hartl"
  user.username              "MichaelHartl"
  user.email                 "mhartl@example.com"
  user.password              "foobar"
  user.password_confirmation "foobar"
  user.send_email            true
end

Factory.sequence :name do |n|
  result = "PersonAAA"
  n.times { result.succ! }
  result
end

Factory.sequence :email do |n|
  "person-#{n}@example.com"
end

Factory.sequence :username do |n|
  "Person#{n}"
end

Factory.define :item do |item|
  item.link_url              "http://www.google.com"
end

Factory.define :item_share do |item_share|
  item_share.note                  "Foo bar"
  item_share.association           :giver, :factory => :user
  item_share.association           :receiver, :factory => :user
  item_share.association           :item, :factory => :item
end

Хорошо, так что вернемся кпровал.Похоже, что RSpec по какой-то причине не может передать имя пользователя (которое я ожидал бы указать в params [: item_share] [: receive_username]) в контроллер (при тестировании).Я перепробовал несколько вещей и все еще в тупике.Вот что я попробовал:

  • Я использую params_username.to_s.downcase в контроллере перед поиском пользователя.Я добавил это, и это помогло (RSpec провалил больше тестов, прежде чем я добавил это), но не решил эту проблему.
  • Я пытался добавить: receive_username в строку attr_accessible в модели item_share.rb.
  • Я пытался связываться с валидациями в модели item_share.rb (например, изменение строки validates: receive на validates_associated: receive и некоторые другие варианты такого рода вещей).
  • rake db: test: prepare - не помогло

Как я уже говорил, все работает как на Dev, так и на Prod.Единственный признак того, что что-то «не так» - это сбои RSpec.

Есть идеи, как мне пройти эти тесты?

Спасибо

Джош

1 Ответ

0 голосов
/ 08 мая 2012

Я добавил это в качестве комментария к своему первоначальному вопросу, но это, очевидно, не помечает сам вопрос как "отвеченный".Для потомков вот копия / вставка моего ответа:

Мне кажется, я обнаружил проблему - я неправильно определил параметры в моей переменной @attr.У меня было

# item_shares_controller_spec.rb
describe ItemSharesController do
  render_views

  describe "POST 'create'" do

    before(:each) do
      @user = test_sign_in(Factory(:user))
    end

    describe "success" do

      before(:each) do
        @attr = {:item =>{:link_url=>@item.link_url}, :item_share =>{:receiver_username=>@receiver.username}, :item_share=>{:note=>"Note"}} 

... когда у меня должно было быть

@attr = {:item =>{:link_url=>@link_url}, :item_share=>{:receiver_username=>@receiver.username, :note=>"Note"}} 

Обратите внимание, что я определял: item_share дважды в определении @attr.Я думаю, что RSpec игнорировал первый :item_share параметр и принимал только второй, :note.

...