Rspec + Devise + Factory Girl Тестирование с ассоциациями - PullRequest
2 голосов
/ 21 февраля 2012

Я довольно новичок в Rspec и переношу кодовую базу с Rails 3.0.x на Rails 3.1.x и одновременно добавляю тестирование. Я смог заставить работать базовые тесты контроллера, но после начала интеграции Devise у меня начались проблемы. В частности, у меня есть объект Devise, называемый User, который принадлежит Company, а Company имеет множество Communities. Я пытаюсь написать тесты для контроллера Communities, и при попытке ссылки по ассоциации (например, controller.current_user.communities) я не могу ссылаться на объекты Factory, которые я создал. Когда я пытаюсь проверить, я получаю следующую (или похожую) ошибку:

No route matches {:id=>nil, :controller=>"communities", :action=>"edit"}

Я уверен, что мне не хватает чего-то базового, связанного с Rspec / Factory_Girl, но любая помощь будет принята с благодарностью.


Пример установки для тестирования действия edit для Communities выглядит следующим образом:

/ конфигурации / routes.rb

Units::Application.routes.draw do

  devise_for :users

  resources :companies
  resources :communities

  ...

  root :to => 'companies#index'

end

/ приложение / модели / user.rb

class User < ActiveRecord::Base
  belongs_to :company
  has_many :communities, :through => :company
  ...
end

/ приложение / модели / company.rb

class Company < ActiveRecord::Base
  has_many :users
  has_many :communities
  ...
end

/ приложение / модели / community.rb

class Community < ActiveRecord::Base
  belongs_to :company
  ...
end

/ спецификации / контроллеры / communities_controller_spec.rb

require 'spec_helper'

describe CommunitiesController do
  render_views
  login_user
  ...
  context "GET #edit" do
    before(:each) do
      @company = controller.current_user.company
      @community = controller.current_user.communities.new[Factory.build(:community, :company => @company)]
      controller.current_user.company.communities.should_receive(:find).with(:company => @company)
    end

    it "should be successful" do
      get :edit, :id => @community
      response.should be_successful
    end

    it "should find a specific community" do
      get :edit, :id => @community
      assigns(:community).should eq(@community)
    end
  end
  ...
end

/ приложение / контроллеры / communities_controller.rb

class CommunitiesController < ApplicationController
  ...
  def edit
    @community = current_user.communities.find(params[:id])
  end
  ...
end

/ спецификация / поддержка / controller_macros.rb

module ControllerMacros
  def login_user
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      company = Factory.create(:company)
      user = Factory.create(:user, :company => company)
      # user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
      sign_in user
    end
  end
end

1 Ответ

3 голосов
/ 26 февраля 2012

Ошибка: «Не найдено ни одного маршрута {: id => nil,: controller =>« community »,: action =>« edit »}» является результатом строки в вашем блоке before (: each), которая читает:

@community = controller.current_user.communities.new[Factory.build(:community, :company => @company)]

Обратите внимание, что Factory.build отличается от Factory.create.Build создает новый экземпляр объекта, но фактически не сохраняет его в базе данных.Кроме того, вы создаете новый экземпляр сообщества с этим новым экземпляром Factory, не сохраняя его.Я бы предложил создать ваше сообщество следующим образом:

   @community = Factory.create(:community, :company => @company)

Позже вы берете эту переменную @community и передаете ее в своем тесте методу get:

get :edit, :id => @community

Потому чтосообщество не сохранено, у него нет идентификатора.Он существует только в памяти.Кроме того, правильнее было бы отправить get id объекта @community (после того, как вы обновили Factory.build для чтения Factory.create):

get :edit, :id => @community.id

Во-вторых, ваша ассоциация не выглядитв соответствии с логикой в ​​вашем контроллере на линии:

controller.current_user.company.communities.should_receive(:find).with(:company => @company)

В вашем контроллере вы находите сообщество с помощью цепочки через current_user.communities, а не current_user.company.communities.Я бы порекомендовал удалить этот тест, так как вы хотите проверить результат, а не реализацию.Это делает тесты слишком хрупкими для изменений кода.

И последнее, я хотел бы отметить, что у вас есть несколько утверждений в ваших тестах.Вы утверждаете в своем before (: each):

controller.current_user.company.communities.should_receive(:find).with(:company => @company)

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

it "should find communities" do
    controller.current_user.company.communities.should_receive(:find).with(:company => @company)
end

Однако, как я упоминал выше, я не думаю, что этот тест является хорошей идеей, так какслишком тесно связан с реализацией того, что вы тестируете.

...