'Capybara and Factorybot - created data does not appear

I am facing a wired issue, factoryBot is creating a record in Db, but when capybara try to access it, there is no record in HTML.

I tried debugging with "byebug", on prompt, when I say @topics => It gives me Nil data. (@topic is instance variable in topics_controller.rb -> index method )

If I do "Topic.all.first" it will show me correct record of Topic with an expected random name that is -> "Toys & Garden" If I do "random_topic.name" -> "Toys & Garden"

I have somewhat similar setup in other feature i.e "account creation feature", it is working fine in there. Any pointer or help would be highly appreciated.

My factory file is

FactoryBot.define do
  factory :topic do
    name { Faker::Commerce.department(2, true) }
    archived false
  end
end

My Feature spec file looks like below

require 'rails_helper'

describe "topics" do
  let(:user) {create(:user)} 

  before do 
    sign_user_in(user)  #support fuction to sign user in

  end 

  it "allows topics to be edited" do
    random_topic = create(:topic)
    visit topics_path   # /topics/ 
    expect(page).to have_text random_topic.name   # Error1 

    click_edit_topic_button random_topic.name  # Another support fuction 
    random_topic_name_2 = Faker::Commerce.department(2, true)
    fill_in "Name", with: random_topic_name_2
    check "Archived"
    click_button "Update Topic"

    expect(page).to have_text "Topic updated!"
    expect(page).to have_text random_topic_name_2
  end
end

I get the error on line marked as "Error 1" , please note that "Toys & Garden" is sample name generated by Faker Gem.

Failure/Error: expect(page).to have_text random_topic.name
       expected #has_text?("Toys & Garden") to return true, got false

my Rails helper(rails_helper.rb) file setup is as below.

# This file is copied to spec/ when you run 'rails generate rspec:install'

require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'database_cleaner'
require 'capybara/rspec'
require 'shoulda/matchers'
require 'email_spec'
require "email_spec/rspec"


Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

ActiveRecord::Migration.maintain_test_schema!

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

# This is for setting up Capybara right host.
# https://stackoverflow.com/questions/6536503/capybara-with-subdomains-default-host 
def set_host (host)
  default_url_options[:host] = host
  Capybara.app_host = "http://" + host
end

RSpec.configure do |config|

  config.include Capybara::DSL

  config.include Rails.application.routes.url_helpers

  config.include FactoryBot::Syntax::Methods
  config.include EmailSpec::Helpers
  config.include EmailSpec::Matchers

  config.order = "random"

  config.use_transactional_fixtures = false

  config.before(:each) do
    set_host "lvh.me:3000"
  end

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end

My Topics Controller file is something like below

class TopicsController < ApplicationController

  layout 'dashboard'
  before_action :authenticate_user!


  def index
    @topics = Topic.all
  end

end

Updated with @smallbutton.com comments, but issue continues.

SOLVED

I am using apartment gem and hence topic was getting created in public schema while test was looking into a respective tenant.

As per @thomas suggestion, I modified the code:

  before do 
    set_subdomain(account.subdomain)
    sign_user_in(user)  #support fuction to sign user in
    Apartment::Tenant.switch!(account.subdomain)
  end 


Solution 1:[1]

When this happens it's generally caused by one of a few things

  1. The record isn't actually being created

    From your code it doesn't appear to be that

  2. The record is being created in one transaction while the app is running in a different transaction.

    You appear to have database_cleaner configured correctly, and this shouldn't be an issue in your case, however you should be using - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example - rather than the configuration you have (which depends on the :js metadata rather than checking which driver is actually being used)

  3. The user has configured Capybara to hit a different server than the one the test is actually running on.

    Here we appear to have an issue, since you're configuring Capybara.app_host to be 'lvh.me:3000'. Port 3000 is what your dev app is generally run on, while Capybara (by default) starts your app on a random port for tests. This probably means your tests are actually running against your dev app/db instance which has nothing to do with the test app/db instance, and therefore the tests won't see anything you create in your tests. Remove the setting of Capybara.app_host unless you have an actual need for its setting (in which case remove the port from your app_host setting and enable Capybara.always_include_server_port)

This all being said, since you're using Rails 5.1+ database_cleaner should not be needed anymore. You should be able to remove all of database_cleaner, reenable use_transactional_fixtures, and set Capybara.server = :puma and have things work fine (still would need to fix the app_host setting)

Solution 2:[2]

Your record isn't persisted when you visit the page, so it's normal that it return false.

Try the following :

require 'rails_helper'

describe "topics" do
  let(:user) {create(:user)}
  let!(:random_topic) {create(:topic)}

  before do 
    sign_user_in(user)  #support fuction to sign user in
    visit topics_path   # /topics/ 
  end 

  it "allows topics to be edited" do
    expect(page).to have_text random_topic.name   # Error1 

    click_edit_topic_button random_topic.name  # Another support fuction 
    random_topic_name_2 = Faker::Commerce.department(2, true)
    fill_in "Name", with: random_topic_name_2
    check "Archived"
    click_button "Update Topic"

    expect(page).to have_text "Topic updated!"
    expect(page).to have_text random_topic_name_2
  end
end

Note that random_topic is extracted in a let! (more info about the difference between let and let! : https://relishapp.com/rspec/rspec-core/v/2-5/docs/helper-methods/let-and-let !

Solution 3:[3]

You should create the record before you visit the page. Currently you do it the other way around so your record cannot appear.

Solution 4:[4]

People having this kind of issue should definitly try this:

instance.reload after clicking and before using "expect".

This solved my issue I've been stuck on for a whole day ...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Thomas Walpole
Solution 2 Philippe B.
Solution 3 smallbutton
Solution 4 Sunamin34