Search code examples
ruby-on-rails-3rspeccapybararspec-rails

nested feature in capybara 2.0+


i want to do something like this:

feature "sign-up" do
  before {visit signup_path}
  let(:submit) {"Create my account"}

  feature "with invalid information" do
    scenario "should not create a user" do
      expect {click_button submit}.not_to change(User, :count)
    end
  end

  feature "with valid information" do
    scenario "should create a user" do
      fill_in "Name",         with: "test name"
      fill_in "Email",        with: "[email protected]"
      fill_in "Password",     with: "password"
      fill_in "Confirmation", with: "password"
      expect {click_button submit}.to change(User, :count).by(1)
    end
  end
end

but when i run rspec i get

in `block in <top (required)>': undefined method `feature' for #<Class:0x000000039e0018> (NoMethodError)

if i change it a bit to look like this it works:

  feature "with invalid information" do
    before {visit signup_path}
    let(:submit) {"Create my account"}

    scenario "should not create a user" do
      expect {click_button submit}.not_to change(User, :count)
    end
  end

  feature "with valid information" do
    before {visit signup_path}
    let(:submit) {"Create my account"}

    scenario "should create a user" do
      fill_in "Name",         with: "test name"
      fill_in "Email",        with: "[email protected]"
      fill_in "Password",     with: "nirnir"
      fill_in "Confirmation", with: "nirnir"
      expect {click_button submit}.to change(User, :count).by(1)
    end
  end

EDIT:

plus, the following code works(describe nested inside feature) - but is it wrong in any way?

feature "sign-up" do
  background {visit signup_path}
  given(:submit) {"Create my account"}

  scenario "with invalid information" do
    expect {click_button submit}.not_to change(User, :count)
  end

  describe "with valid information" do
    background do
      fill_in "Name",         with: "test name"
      fill_in "Email",        with: "[email protected]"
      fill_in "Password",     with: "password"
      fill_in "Confirmation", with: "password"
    end

    scenario { expect {click_button submit}.to change(User, :count).by(1) }

    scenario "after submission" do 
      click_button submit
      page.html.should have_content("Registration successful")
    end
  end
end

Solution

  • EDIT (23/01/2014): Nested features are available since version 2.2.1. See here

    EDIT (24/07/2013): Nested features will be allowed in Capybara > 2.1.0. See here

    You can't. This is what the mantainer of the gem says about it

    I guess you could call this a limitation. feature can not be nested. You can use either context or describe instead, but I would suggest not going wild with these, it tends to make tests pretty unreadable.

    In some other cases the convenience of this might be argued but in this specific one you should use scenario instead of the nested feature.

    Also if you want to consistent and use the new DSL everywhere, use background instead of before and given instead of let. Like this:

    feature "sign-up" do
      background {visit signup_path}
      given(:submit) {"Create my account"}
    
      scenario "with invalid information" do
        expect {click_button submit}.not_to change(User, :count)
      end
    
      scenario "with valid information" do
        fill_in "Name",         with: "test name"
        fill_in "Email",        with: "[email protected]"
        fill_in "Password",     with: "password"
        fill_in "Confirmation", with: "password"
        expect {click_button submit}.to change(User, :count).by(1)
      end
    end
    

    You have to remove the it because scenario is just an alias for it and you cannot nest it either.

    Or you can always switch back to the old DSL if you find it more readable. In that case I would do something like this:

    describe "sign-up" do
      before {visit signup_path}
      let(:submit) {"Create my account"}
    
      context "with invalid information" do
        it "does not create a user" do
          expect {click_button submit}.not_to change(User, :count)
        end
      end
    
      context "with valid information" do
        before
          fill_in "Name",         with: "test name"
          fill_in "Email",        with: "[email protected]"
          fill_in "Password",     with: "password"
          fill_in "Confirmation", with: "password"
        end
        it "creates a user" do
          expect {click_button submit}.to change(User, :count).by(1)
        end
      end
    
    end
    

    But as long as the spec checks what it has to check, you should be fine anyways. The rest is all a matter of style, readability and good practices, which are important but more open to discussion and disagrement. In this case the author of the gem didn't allow nested feature's. Maybe for readability or maybe didn't feel it was needed or maybe didn't think about it... If you really want to nest features you can always try to implement it and pull request it.