I've inherited a Rails application, and I'd like to add some unit tests (currently there were just a couple old broken capybara tests). I'm new to Rails so I want to start somewhere very basic. I have a users_controller.rb file, and I've added /test/controllers/users_controller_test.rb with the following code:
require "test_helper"
puts "right before the test"
def test_view_user_info
puts "inside of the test"
get "/users/12/edit"
assert_response :success
end
test_helper.rb just has:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
class ActiveSupport::TestCase
include Rails.application.routes.url_helpers
end
To run the test in Rubymine, I right-click the test select "Run 'Run test 'users_cont...' (and I also get the same result when I run 'rake test')
The problem: I get exit code 0, but we don't appear to run the code inside the test! I confirmed with with the 'puts' statements- the output only shows the one outside of the test, and not the one inside:
/Users/drkaplan/.rbenv/shims/bundle exec ruby -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) -Itest /Users/drkaplan/broad-count-me-in/test/controllers/users_controller_test.rb
right before the test
Process finished with exit code 0
I showed it to someone experienced with Rails who has never seen this before: it's as if my test file is being run as a script and not invoking the test blocks. The gemfile does include capybara in the test group (and I've added mintiest-rails for this new test) so I'm wondering if there may be some conflict between the two that prevents my test code from executing. Here's the relevant part of my gemfile:
group :test do
gem 'shoulda-matchers', require: false
gem 'capybara'
gem 'capybara-email'
gem 'selenium-webdriver'
gem 'minitest-rails'
end
Another weird symptom: when I try running the test individually with --help, I get only the output from puts statements around the test, and none of the typical help options
ruby -I test test/controllers/users_controller_test.rb --help
Anyone see what the problem may be, or had this symptom before?
Related
Rails 7.0.4
Rspec 3.11.0
Capybara 3.37.1
selenium-webdriver (gem) 4.5.0
with Ruby-debug (gem 'debug', platforms: %i[ mri mingw x64_mingw ]) in Gemfile
whenever I try to add Capybara.using_session call, my suite hangs.
it hangs after it completes all the specs, even when passing.
my driver is set to :
Capybara.default_driver = :selenium_chrome_headless
but I noticed it happens if I change the driver to Capybara.default_driver = :selenium_chrome also
here's the spec that reproduces it:
require 'rails_helper'
describe "client app", type: :feature do
describe "when starting the experience", type: :feature do
# TODO: figure out why capy is hanging here
it "can load the home page" do
# THIS CAUSES AFTER-RSPEC HANG even though all specs pass
Capybara.using_session("client session") do
visit "/"
end
end
it "loads with a client id" do
visit '/'
end
end
end
I have isolated it to these THREE conditions :
using Capybara.using_session and visiting a page using visit inside the using_session block (if I remove either the visit or the using_session, it works)
In another test in the same suite, visiting any other page using visit (if I remove the visit call in the other spec, it works)
the debug gem (ruby-debug) (if I remove gem 'debug', platforms: %i[ mri mingw x64_mingw ] from the Gemfile)
This is a very strange bug.
Capy hang looks like:
my spec/rails_helper.rb file is
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
Capybara.default_driver = :selenium_chrome_headless
IMPORTANT: Although I have isolated it this far, I am strangely unable to isolate it any further or even able to reproduce it in a new Rails app, even though this Rails app is just a month old.
Here, I have taken the app where it is broken and removed everything else (really, I removed EVERYTHING and the hang still happens).
I have compared the remaining code carefully to a newly generated app and bizarrely I cannot reproduce the same bug (Using the exact same spec and setup) in a new app, so it must involve a 4th unknown variable I cannot see yet.
REPRODUCTION
Bare-bones reproduction app here:
https://github.com/jasonfb/StrangeCapybaraHang
This app was created as a FORK of my app, then I removed everything non-essential. Notice that it contains almost nothing, and only 1 spec.
When I run rspec on this app (broken), see "DEBUGGER: Attaching after process 77726 fork to child process 77733" AND the suite hangs after it runs
Here, on this 2nd attempt to recreate the problem, I tried to go forward to create the bug: https://github.com/jasonfb/StrangeCapyHangForward
I setup a new Rails app and then attempted to recreate all of the conditions explained above to produce the bug... BUT... I can't reproduce the bug in this new app!!
So that means even though I've identified the 3 elements of the bug, there MUST be a 4th element I am not seeing yet.
Notice that in this app, it has same exact spec (see spec/system/test_capy_hand_spec.rb), the same exact rails_helper file, the same exact Gems, etc as the other repo where the bug manifests.
However, on this app, I never see "DEBUGGER: Attaching after process 77726 fork to child process...." even when debug Gem is in the Gemfile. Why is that?
I also DO NOT see the hang.
2022-12-15 SOLVED!
TL;DR
The issue is in the selenium-webdriver gem. Upgrade from 4.5.0 in your StrangeCapybaraHang project to 4.7.1 and it should solve your problem immediately.
Reproducing from Scratch
Fork the StrangeCapyHangForward repo.
Run ./bin/setup && yarn build. Note, if esbuild is not globally installed, you'll need to add it with yarn add esbuild since it's missing from package.json.
Downgrade the selenium-webdriver gem to the same version that is in StrangeCapybaraHang. For example, in your Gemfile's :test group: gem 'selenium-webdriver', '4.5.0'
bundle install && rspec. It hangs.
Example of Gemfile with not working selenium-webdriver gem added
# Gemfile of StrangeCapyHangForward
group :development, :test do
gem 'debug', platforms: %i[ mri mingw x64_mingw ]
gem 'rspec-rails'
# add the older version of the gem
gem 'selenium-webdriver', '4.5.0'
end
Steps to Fix
Add gem 'selenium-webdriver', '4.7.1' to your Gemfile in StrangeCapyHangForward.
bundle install
Run rspec. No more hang!
Note: You may have to grep and kill existing rspec processes. For example:
ps -ax | grep rspec
# take process ids for all the above and run (substituting 71529 for your pids):
kill -9 71529
Example Gemfile with working selenium-webdriver gem added
# Example Gemfile with working version of selenium-webdriver
group :development, :test do
gem 'debug', platforms: %i[ mri mingw x64_mingw ]
gem 'rspec-rails'
# add selenium-webdrive gem coded to 4.7.1
gem 'selenium-webdriver', '4.7.1'
end
I believe the issue is related to concurrency and rspec not properly killing all the test processes, although this is difficult to confirm. We had big trouble with our test suite and concurrency, too, until we forcibly upgraded many of our gems (including dependencies).
For example, before upgrading run rspec a few times and grep your processes: ps -ax | grep rspec. You'll see lots of existing ones there. I did check the selenium-webdrive changelog to see if there was anything obvious that would cause this, but I didn't see anything.
Anyways, hope this helps! Good luck!
Try removing the debug gem, as it is probably causing the issue with capybara.
On our application we use RSpec, Capybara and Capybara-screenshot. All things work fine except cleaning tmp/screenshots folder. The project is configured with default settings, so images are saved in the screenshots folder. New screenshots with failed tests are added to the old ones and are difficult to find and identify problems in tests.
Capybara-screenshot has default method prune_strategy to do this, but it didn't work.
After implement a new function, we need to fix/create a test, and then manually empty the folder with screenshots and only then run tests so as not to confuse the old screenshots and the new ones. Is there a way to configure this folder to be automatically cleaned up after running the rspec command, but before running the tests?
# Gemfile
ruby '2.6.6'
gem 'rails', '5.2.0'
group :test do
gem 'capybara'
gem 'capybara-screenshot'
gem 'webdrivers'
end
# spec/rails_helper.rb
require File.expand_path('../config/environment', __dir__)
abort('The Rails environment is running in production mode!') if Rails.env.production? || Rails.env.staging?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'capybara/rspec'
require 'capybara-screenshot/rspec'
Capybara.asset_host = 'http://localhost:5000'
Capybara::Screenshot.prune_strategy = :keep_last_run
# Also tried with this config:
# Capybara::Screenshot.prune_strategy = { keep: 20 }
Thanks for answers!
From the capybara-screenshot gem
def wildcard_path
File.expand_path('*.{html,png}', Screenshot.capybara_root)
end
def prune_with_last_run_strategy
FileUtils.rm_rf(Dir.glob(wildcard_path))
end
So it will remove all html and png files from the Screenshot.capybara_root directory - Obviously that wouldn't work if you're not screenshotting in png or html and if you're changing the directory after pruning has occurred.
Note, that from the code it looks like pruning is only done when the screenshot is saved, so if you have no failures in your run it wouldn't actually prune the screenshots from the previous run. If that's not the behavior you want it looks like you should be able to add an RSpec before(:suite) block that calls Capybara::Screenshot.prune
I'm making a gem that executes Rails commands (rails g model Item for example). When I use it in a Rails project, everything works. The problem is testing it in development outside of a Rails project.
I'm using cucumber with aruba to test if CLI commands execute the proper rails commands and generate the expected files. Unfortunately, when I try to test the behaviour it fails because there are no rails files and the commands require to be run inside of a Rails project in order to work.
I have added a rails dependency to the gemspec:
Gem::Specification.new do |spec|
spec.add_development_dependency 'rails', '~> 5.2.4'
end
I've thought about creating a new rails project on test start and then deleting it after the tests run, but that seems highly inconvenient. Is there a better way to do this?
A technique we use for WickedPDF is in the default rake task, before we run the tests, is to delete & generate a full Rails application in a gitignored subdirectory of the gem.
As a high-level simplified example of this Rakefile, it looks something like this:
Rakefile
require 'rake'
require 'rake/testtask'
# This gets run when you run `bin/rake` or `bundle exec rake` without specifying a task.
task :default => [:generate_dummy_rails_app, :test]
desc 'generate a rails app inside the test directory to get access to it'
task :generate_dummy_rails_app do
if File.exist?('test/dummy/config/environment.rb')
FileUtils.rm_r Dir.glob('test/dummy/')
end
system('rails new test/dummy --database=sqlite3')
system('touch test/dummy/db/schema.rb')
FileUtils.cp 'test/fixtures/database.yml', 'test/dummy/config/'
FileUtils.rm_r Dir.glob('test/dummy/test/*') # clobber existing tests
end
desc 'run tests in the test directory, which includes the generated rails app'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
Then, in test/test_helper.rb, we require the generated Rails app, which loads Rails itself and it's environment:
test/test_helper.rb
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../dummy/config/environment.rb', __FILE__)
require 'test/unit' # or possibly rspec/minispec
# Tests can go here, or other test files can require this file to have the Rails environment available to them.
# Some tests may need to copy assets/fixtures/controllers into the dummy app before being run. That can happen here, or in your test setup.
You could skip parts of Rails that aren't needed by customizing the command that generates the app. For example, your gem may not need a database at all or a lot of things by default, so you command could be customized for a simpler app. Something like this maybe:
system("rails new test/dummy --skip-active-record \
--skip-active-storage --skip-action-cable --skip-webpack-install \
--skip-git --skip-sprockets --skip-javascript --skip-turbolinks")
In the WickedPDF project, we wanted to test across a wide range of "default" Rails installs, so we don't customize the command much, but that may generate much more than what you need to test some generator tasks.
WickedPDF also tests against multiple versions of Rails with TravisCI and multiple Gemfiles, but this could also be accomplished with the Appraisal gem that Luke suggested in this thread.
Check out Thoughbot's Appraisal gem:
Appraisal integrates with bundler and rake to test your library against different versions of dependencies in repeatable scenarios called "appraisals."
Here is a guide on how to set it up, including setting up a micro Rails app within your tests dir.
I am using VsCode to write and edit some rspec tests.
What I would like to be able to do is run a specific 'it' or 'describe' block in debug mode.
At this time I can run the rspec/spec file in debug mode but it executes all of the tests.
I have install 'Rails run Spec' extension which allows me to execute a specific 'it' or 'describe' block without the ability to debug.
Ideally I would like both options married together.
I have done some digging but not able to find anything that fits my scenario.
Any help would be greatly appreciated.
Joe
Add pry-byebug to your gemfile and run bundle install
# Gemfile
gem 'pry-byebug'
Then, whenever you want to inspect a test, add binding.pry inside the test.
# some_spec.rb
it "is not behaving how I want it to" do
binding.pry
expect(my_var).to eq(some_val)
end
I started making a Rails 3.1 engine, and I'm having a hard time testing it using rspec.
First of all, if I run rails g integration_test whatever it creates a regular integration test in tests/integration instead of spec/requests (the rspec-rails gem is installed and required as a development dependency in the gemspec file)
Also, when I run a spec test I get an error saying the table corresponding to the model I'm testing has not been created. I tried rake engine_name:install:migrations and running rake db:migrate from inside the dummy app, and I get a "table already exists" error.
Everything just seems disconnected, I feel I'm missing something here to make the rspec gem work seamlessly as it usually does with full rails applications.
I followed all the changes from here http://rubyx.com/2011/03/01/start-your-engines and I can test the engine manually by launching the dummy app via the console as shown here http://railscasts.com/episodes/277-mountable-engines.
Is there a way to make rspec the default for testing a rails 3.1 engine?
I am using RSpec with a Rails engine without issues.
I created my plugin using the following switches: -T --full --dummy-path=spec/dummy.
-T excludes test/unit
--full indicates that the plugin is an engine
--dummy-path is simply so that we don't get a test directory (the
default is test/dummy).
From there I used the spec_helper from the "start your engines" article:
# Configure Rails Envinronment
ENV["RAILS_ENV"] = "test"
require File.expand_path("../dummy/config/environment.rb", __FILE__)
require 'rspec/rails'
ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each {|f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = true
end
For the generators. I add a config.generators block to my engine.rb file like so:
module MyEngine
class Engine < Rails::Engine
config.generators do |g|
g.test_framework :rspec, :view_specs => false
end
end
end
With that, I'm able to get rspec tests when running a generator like the model generator.
As for the DB, is your database.yml file set up correctly? Did you load the test environment, e.g. rake db:test:clone or rake db:migrate RAILS_ENV=test? My guess is that RSpec can't see your tables because there isn't a test database set up.
I was looking for the same answer and I found the combustion gem* which promise to setup a full environment for spec'ing your engine in a simpler way. Just add
gem.add_development_dependency 'combustion', '~> 0.3.1'
to your gemspec and run
bundle exec combust
to reproduce a full rails app in your spec directory.
*I haven't tried it yet...