Rails app disable request forgery protection in test environment by default. So csrf token dose not appear in app using ` <%= csrf_meta_tags %> or form_for`

config/environments/test.rb

# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false

If you want to test or use csrf token you had better use mock to protect_against_forgery in specs.

allow_any_instance_of(ActionController::Base).to receive(:protect_against_forgery?).and_return(true)

Try method

If you get user.name and user is nil the NoMethodError is occurred.

# user is nil
name = user.name # NoMethodError !!!!1

You had better use try method. You can get name value if user is not nil and if user is nil you can get ‘nil’ instead. The NoMethodError is not occurred.

# user is nil
name = user.try(:name) # return nil. not occurred NoMethodError
# user is not nil
name = user.try(:name) # return name value.

You can set default value if user is nil like this.

name = user.try(:name) || "default name"

About Capybara.default_wait_time

According to api document

find will wait for a set amount of time and continuously retry finding the element until either the element is found or the time expires. The length of time find will wait is controlled through Capybara.default_wait_time and defaults to 2 seconds.

ATTENTION

You had better not set much time to Capybara_default_wait_time.

For example, if you set 10 seconds to Capybara.default_wait_time when you want to check the element dose not exist. Test time can be long. Because Capybara wait to check the element dose not exist in each test.

# in rspec.rb
Capybara.default_wait_time = 10

# in feature test
expect(page).to have_no_selector(:css, "p a#doesnotexist") # wait 10 seconds

Solution

If you need more than 2 seconds(default value in Capybara.default_wait_time) to wait to check you can set wait option to find instead.

expect(page).to have_selector(:css, "p a#doesnotexist", wait: 3)

Can not find element PART 1

Capybara can not find invisible element by default. You need set visible option like this.

find(selector, visible: false)
  • true - only finds visible elements.
  • false - finds invisible and visible elements.
  • :all - same as false; finds visible and invisible elements.
  • :hidden - only finds invisible elements.
  • :visible - same as true; only finds visible elements.

Can not find element PART 2

Capybara retry to find after 3 seconds by default when Capybara can not find the element. (Capybara.default_wait_time) You could need more then 3 seconds to wait to find element. (e.g. the element is appeared after ajax request.)

try to set wait option. :)

## some async logic .....

find(selector, wait: 4) # can find

ATENTION You had better not set this option a lot. Because Capybara stop test to wait so test time can be long.

Can not find element which is inside head tag

As mention above, Capybara can not find invisible element. The elements which is inside head, <script> and <style> can not be found by default too.

So you need to set visible: false option.

Can not find text

As mention above, Capybara can not find invisible element. The text which is inside invisible element can not be got by default too.

You need to set visible option to get text which is inside invisible element.

find(selector, visible: false).text(:all)
  • :all → all text (visible or not)
  • :visible → only visible text

If the result which is test links is not expected You need to check visiblity of that links. You can not test invisible links using have_link so you need to check link text and href attribute individually.

BAD

link_tag = find(selector) # link is invisible
expect(link_tag).to have_link(text, href: "http://stores.jp") # failed

GOOD

link_tag = find(selector, visible: alse) # find invisible link

expect(link_tag).to have_text(:all, text) # check link text
expect(link_tag[:href]).to eq("http://stores.jp") # check href attribute

Can not get screenshot

You need to set js:true option in describe or context

describe "test", js:true do
  it "screenshot test" do
    page.save_screenshot('name.png')
  end
end

Too slow

To visit page in feature test is too slow. You need to reduce access page.

Page object is old

check this article

Outline

When let / let! syntax is called in Rspec. ?

let

Use let to define a memoized helper method and you can execute it when you want to do. But it can be called once in example.

let!

It is similar to let. It force the method’s invocation before each example. (let! is similar to before)

Example

require 'rails_helper'

describe "let spec example" do

  let(:let_sample_1) { p "let sample 1" }

  it "" do
    let_sample_1 # print "let sample 1"
    let_sample_1 # dose not print "let sample 1"
  end

  context "" do
    count = []
    let!(:let_sample_2) { count.push(1) }
    it "" do
      p count # [1]
      p count # [1]
    end
    it "" do
      p count # [1, 1]
      p count # [1, 1]
    end
    context "" do
      it "" do
        p count # [1, 1, 1]
      end
    end
  end

end