[Rspec] Requests & Controllers tests

Witam serdecznie,

dokształcam się obecnie w tworzeniu controller i request testów. W związku z tym stworzyłem prostą stronę formularza kontaktowego i chciałbym się Was zapytać czy robię powyżej wspomniane testy dobrze:

Kontroler:

class StaticsController < ApplicationController
  def contact
    if request.post?
      @contact = Contact.new(params.require(:contact).permit(:name, :email, :subject, :advert, :message))

      if @contact.save
        redirect_to statics_contact_path, notice: 'Contact was successfully created.'
      else
        render action: :contact
      end
    else
      @contact = Contact.new
    end
  end
end

Test Kontrolera:

require 'spec_helper'

describe StaticsController do
  subject { response }

  context "#contact" do

    describe "GET #contact" do
      before(:each) { get :contact }

      it { should be_success }
      it { should render_template :contact }
    end

    describe "POST #contact" do
      let(:attrs) { FactoryGirl.attributes_for(:contact) }

      context "failure" do
        before(:each) { post :contact, contact: attrs.update(name: nil) }

        it { should be_success }
        it { assert_not_nil assigns(:contact) }
        it { assert_nil assigns(:contact).name }
        it { assert_not_empty assigns(:contact).errors.messages }
        it { should render_template :contact }
      end

      context "success" do
        before(:each) { post :contact, contact: attrs }

        it { should be_redirect }
        it { should redirect_to statics_contact_path }
        it { expect { post :contact, contact: attrs }.to change(Contact, :count).by(1) }
        it { expect(flash[:notice]).to_not be_nil }
      end
    end
  end
end

Test Requestu:

require 'spec_helper'
require 'faker'

describe "Static pages" do

  subject { page }
  
  
  shared_examples_for "shared" do
    it { should have_selector('h1', text: heading) }
    it { should have_title(page_title) }
  end


  describe "Contact page" do
    let(:heading)    { 'Statics#contact' }
    let(:page_title) { '' }

    before { visit statics_contact_path }


    context "GET page" do
      it { should have_selector 'h1' }
      it { should have_button('Send') }

      it_should_behave_like "shared"
    end


    context "POST page" do
      context "with incomplete form" do
        before do 
          within("#contact") do
            fill_in "contact[name]", with: Faker::Name.name
            fill_in "contact[email]", with: Faker::Internet.email
          end

          click_button "Send"
        end

        it { expect{ click_button "Send" }.not_to change(Contact, :count) }
        it { should have_content '4 errors prohibited this contact from being saved' }
      end


      context "with complete form" do
        before do 
          within("#contact") do
            fill_in "contact[name]", with: Faker::Name.name
            fill_in "contact[email]", with: Faker::Internet.email
            fill_in "contact[message]", with: Faker::Lorem.sentence(150)
            select find("option[value='#{(1..7).to_a.sample}']").text, from: "contact[subject]"
          end

          click_button "Send"
        end

        it { expect(Contact.count).to be 1 }
        it { expect(page.status_code).to be 200 }
      end
    end
  end
end

Prosiłbym o wytknięcie ewentualnych błędów. Ponadto wszelkie porady odnośnie tego typu testów będą mile widziane.

Tak na marginesie chciałbym Ci zwrócić uwagę, że nazwa Twojego kontrolera (StaticsController) sugeruje, że zajmuje się on statycznymi stronami, a testujesz ewidentnie coś dynamicznego.

To jest nieistotne na tym etapie. Sam kontroler został wygenerowany na szybko - bardziej mnie interesuje zagadnienie związane z poprawnym pisaniem testów kontrolerów i requestów.

Źle.
Konwencja jest taka że POST i GET służą do różnych czynności (co zresztą potwierdza twój kod tym wszystkoobejmującym ifem) i powinny być obsługiwane przez dwie różne metody.
Poczytaj o kontrolerach, routingu i zasobach (resources).

it { should be_success }
it { should render_template :contact }

describe 'contact' do
  subject { assigns(:contact) }

  it { should_not be_nil }
  its(:name) { should be_nil }
  its(:errors) { should be_present }
end

params.require(:contact).permit(:name, :email, :subject, :advert, :message)

Wydziel to do metody np. contact_params, nie ma sensu pakować tego bezpośrednio w konstruktorze

To fakt, złe nawyki z innych języków :stuck_out_tongue: Nie mniej jednak chodzi mi tylko i wyłącznie o poprawność testów. Tzn czy testy zostały napisane w miarę dobrze (bo jeszcze nie do końca rozumiem podziału testów Requests, Controllers, Features itp - dla mnie mogły by być to testy kontrolera).

dzięki, to mi się przyda :slight_smile: