Как сохранить управляемость тестов RSpec - PullRequest
4 голосов
/ 23 февраля 2012

Чтобы изучить RoR, я начал использовать превосходное Rails Tutorial .Пока все хорошо, хотя я заметил, что тесты RSpec быстро превращаются в неразбериху.Ниже приведен пример интеграционных тестов для session_controller.rb.Пока я продолжу, это продлится только дольше.

Есть ли логический способ разделить и разбить эти тесты на более мелкие куски?Как бы вы поступили об этом и на основании каких критериев?Примеры приветствуются.

Пример:

require 'spec_helper'

describe "AuthenticationPages" do
  subject { page }

  describe "signin" do
    before { visit signin_path }

    it { should have_selector('h1',  text: 'Sign in') }
    it { should have_selector('title', text: full_title('Sign in')) }

    describe "with invalid information" do
      before { click_button "Sign in" }

      it { should have_selector('title', text: full_title('Sign in')) }
      it { should have_selector('div.flash.error', text: 'Invalid') }
      it { should_not have_link('Profile', href: signout_path ) }
      it { should_not have_link('Settings', href: edit_user_path) }

      describe "after visiting another page" do
        before { click_link "Home" }
        it { should_not have_selector('div.flash.error') }
      end
    end

    describe "with valid information" do
      let(:user) { FactoryGirl.create(:user) }
      before do
        fill_in "Email",   with: user.email
        fill_in "Password",  with: user.password
        click_button "Sign in"
      end

      it { should have_selector('title', text: user.name) }
      it { should have_link('Profile', href: user_path(user)) }
      it { should have_link('Settings', href: edit_user_path(user)) }
      it { should have_link('Users', href: users_path) }
      it { should have_link('Sign out', href: signout_path) }

      it { should_not have_link('Sign in', href: signin_path) }

      describe "visiting the sign up page" do
        before { visit sign_up_path }
        it { should_not have_selector('h1', text: 'Sign Up') }
        it { should_not have_selector('title', text: full_title('Sign Up')) }
      end

      describe "submitting to the create action" do
        before { post users_path(user) }
        specify { response.should redirect_to(user_path(user)) }
      end

      describe "followed by signout" do
        before { click_link "Sign out" }
        it { should have_link('Sign in') }
      end
    end
  end

  describe "authorization" do

    describe "for non-signed-in users" do
      let(:user) { FactoryGirl.create(:user) }

      describe "in the users controller" do

        describe "visiting the edit page" do
          before { visit edit_user_path(user) }
          it { should have_selector('title', text: 'Sign in') }
        end

        describe "submitting to the update action" do
          before { put user_path(user) }
          specify { response.should redirect_to(signin_path) }
        end
      end

      describe "visiting user index" do
        before { visit users_path }
        it { should have_selector('title', text: 'Sign in') }
      end

      describe "when attempting to visit a protected page" do
        before do
          visit edit_user_path(user)
          sign_in user
        end

        describe "after signing in" do
          it "should render the desired protected page" do
            page.should have_selector('title', text: 'Edit user')
          end

          describe "when signing in again" do
            before do
              visit signin_path
              sign_in user
            end

            it "should render the default (profile) page" do
              page.should have_selector('title', text: user.name)
            end
          end
        end
      end
    end

    describe "as wrong user" do
      let(:user)        { FactoryGirl.create(:user) }
      let(:wrong_user)  { FactoryGirl.create(:user, email: "wrong@example.com") }
      before            { sign_in user }

      describe "visiting users#edit page" do
        before { visit edit_user_path(wrong_user) }
        it { should have_selector('title', text: 'Sample App') }
      end

      describe "submitting a PUT request to the users#update action" do
        before { put user_path(wrong_user) }
        specify { response.should redirect_to(root_path) }
      end
    end

    describe "as non-admin user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:non_admin) { FactoryGirl.create(:user) }

      before { sign_in non_admin }

      describe "submitting a DELETE request to the Users#destroy action" do
        before { delete user_path(user) }
        specify { response.should redirect_to(root_path) }
      end
    end
  end
end

1 Ответ

4 голосов
/ 24 февраля 2012

Ну, учитывая, что вы уже используете RSpec с musta (верно?), Я думаю, вы достигли высокого уровня читабельности и управляемости.Вы всегда можете разделить эту спецификацию на более мелкие части, но вы должны спросить себя, действительно ли необходимо разделение тестового кода для одного контроллера?У вас есть много разделов describe, которые хорошо разбираются в структурировании тестов.Если что-то не получается, RSpec всегда даст вам точный номер строки, чтобы вы могли сразу перейти к нему и исправить его.

Что касается дополнительной читабельности, я заметил, что вы используете пустые строки после describe разделов.Некоторые люди также любят вставлять пустые строки перед операторами end.Я также рекомендую написать, в каком блоке вы заканчиваете, с помощью оператора end, например:

describe "GET /posts" do
#[...]
end #     GET /posts

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

Надеюсь, это убедит вас, что у вас уже есть отличный способ организовать свой код.Я не думаю, что тесты разделения, нацеленные на одну и ту же функциональность / объект / цель, имеют какой-то смысл просто держать его под < 100 строками или около того.

Обновление

Я недавно прочитал article , в котором DHH гласит, что RSpec неоправданно сложен и что test/unit читабелен и прост в обслуживании.Я подумал, что вы захотите узнать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...