Is possible to test before Rails loaded? - ruby-on-rails

I'm creating a gem and this gem depends Rails is loaded. I need to create a scenario when Rails is not loaded. I have this code in my gem as an example:
if defined?(Rails) && Rails.application
#some code
end
I've tried to create a stub, but it seems Rails restarts the application method when we set it with nil. https://github.com/rails/rails/blob/fbe2433be6e052a1acac63c7faf287c52ed3c5ba/railties/lib/rails.rb#L38
Rails.stub(:application, nil) do
# test
end

Easy. Just setup a bootstapper file for your test that does not load Rails.
# spec/barebones_helper.rb
RSpec.configure do |config|
end
# optionally load your dependencies:
# Bundler.require(:some_group)
# spec/lib/mygem_spec.rb
require 'barebones_helper'
In fact rspec-rails already generates two separate files*:
spec_helper.rb - just configures rspec.
rails_helper.rb requires spec_helper.rb and then boots Rails through require File.expand_path('../config/environment', __dir__).
Simply changing require 'rails_helper' to require 'spec_helper' will change the test to become executed outside rails.
However be aware that running the tests through Spring will generate strange results as it will keep Rails running in the background. If you're using a spring binstub you can disable spring with the DISABLE_SPRING env var.

Related

prune_strategy don't work in capybara-screenshot, folder with screenshots is not automatic cleared

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

How to test a gem that depends on Rails and uses Rails commands

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.

Rspec - where is the extra configuration for test environment?

I am using the default test for different purposes and I have decided to make a specific rspec environment configuration for running the test suite.
However, I discovered that upon changing to ENV["RAILS_ENV"] ||= rspec in my rails_helper.rb file, suddenly a LOT of things are going wrong, constants are not being loaded (FactoryGirl, DatabaseCleaner, etc. throw uninitialized constant errors)
My question is, where is the code that loads those guys in test environment ? Since I am planning to use this stage for other purposes than running automatic tests, I'm afraid this "out of nowhere" added configuration might not work well with what I am planning to do.
From the perspective of Rails, the test environment is configured and loaded like any other environment such as production or development. You can see this prefixing RAILS_ENV=test to many of the native Rails commands e.g. RAILS_ENV=test rails c will load the rails console for the test environment, and so on. Similarly, all test-specific configuration within Rails is defined in test.rb in your config/environments folder.
However, when you run your specs with rspec spec, you're actually starting the RSpec runner, which, for most intents and purposes, runs independently of Rails (even with the rspec-rails gem).
By convention, when RSpec starts the first thing it does is read command line args from the .rspec in the current directory, if it exists. Then it runs spec_helper.rb (and also rails_helper.rb for rspec-rails 3+). It's actually the spec_helper.rb which does all the heavy-lifting in loading the Rails environment for your tests, along with any of the modules you're using in tests, such as DatabaseCleaner, FactoryGirl, etc.
If you're wondering how RSpec hooks into Rails, the bulk of it is performed in this line, which bootstraps Rails.
require File.expand_path('../../config/environment', __FILE__)
Now, as to your question, without the ENV['RAILS_ENV'] ||= 'test' statement, the above line will load Rails in the default environment (development), which isn't what you want, since any gems not in the :test group will not be loaded, and environments/test.rb will not be loaded either.
TL;DR
Test configuration is handled by two files: spec/spec_helper.rb (sometimes named rails_helper.rb) and config/environments/test.rb. The former configures RSpec and any objects and modules which will be used specifically within the files used in spec, the latter configures your Rails app itself. Omitting ENV['RAILS_ENV'] ||= test loads the development environment and gemsets instead of the test environment and gemsets, which is why you're getting a ton of errors.
If you are getting uninitialized constant errors for FactoryGirl, DatabaseCleaner etc, you most likely included them to test group in your Gemfile.
You should move them to rspec group, eg:
# Gemfile
group :rspec do
gem 'factory_girl_rails', '~> 4.0'
gem 'faker'
end

How do I use factories from FactoryGirl in rails console

I am using rails console in the development environment and I want to use factories. How can I get access to them?
I have tried require "FactoryGirl" which returns
1.9.3p393 :301 > require "FactoryGirl"
LoadError: cannot load such file -- FactoryGirl
I do this the following way:
Start the rails console in test environment in sandbox mode.
rails console -e test --sandbox
You need this for two reasons:
Any changes you do are rolled back.
If you already have some seed data it might happen that the factories will start the serialization of attributes from 1, but these records might already exist.
Then in the console:
Require FactoryBot (was called FactoryGirl):
require 'factory_bot'
Load the factory definitions:
FactoryBot.find_definitions
Include the FactoryBot methods to avoid prefixing all calls to FB with FactoryBot (create instead of FactoryBot.create):
include FactoryBot::Syntax::Methods
P.S. For fabrication gem you can load the definitions in the rails console with:
Fabrication.manager.load_definitions
Also require 'faker' if you use it.
To solve this problem ensure that the factory bot gem is specifed in your Gemfile similar to this
group :development, :test do
gem 'factory_bot_rails'
end
Then bundle install.
This should make FactoryBot class available in the development console.
Hope this helps.
You need to require 'factory_bot_rails', which is the actual gem that's being used by Rails. That gem will include the Factory Bot library, making FactoryBot available.
You can either do this, or update your Gemfile to require it at startup as in muttonlamb's answer.
If you want to have it available each time you start the console, you can add this piece of code to the top of your config/environments/development.rb:
require 'factory_bot_rails'
require 'faker' # if you're also using faker gem
require 'rails/console/helpers'
Rails::ConsoleMethods.prepend(FactoryBot::Syntax::Methods)
Now you can use the built-in helpers right after starting the console, for example:
company = create(:company)

Testing Rails 3.1 mountable engine with Rspec

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...

Resources