![speed dial for a car or machine showing maximum power](https://uploads.sitepoint.com/wp-content/uploads/2013/12/Fotolia_35034583_Subscription_XXL-300x300.jpg)
The Root of Slow Tests in Rails
Running tests in Rails is usually slow and the slowness often originates from the very first line of most spec files – require 'spec_helper.rb'
.
Take a typical spec_helper.rb
for example (from gitlab):
A simple examination of this file reveals its heavy load duties:
– The whole Rails environment at L14
– Test frameworks and their configuration such as rspec
and capybara
– Test helpers and their configuration, such email_spec
, factorygirl
and webmock
– Test reporters and accelators – simplecov
, spork
That’s a lot of code to load, and it’s loaded regardless of test contexts. Acceptance tests load it, integration tests load it, unit tests load it as well. This one-for-all spec_helper.rb
contributes to unnecessary load time when we need to run any single test.
Do We Really Need That Much?
In the process of Test Driven Development, the most frequent tests we need to run during development are unit tests. We only need to run integration tests and acceptance tests towards the end of a development cycle.
Further more, with good design patterns, most objects would be built upon POROs (Plain Old Ruby Object) such as a Value Object, Policy Object or Form Object. These objects are built upon the framework APIs and could be tested in isolation from the framework.
To test these POROs, we don’t need to load the whole Rails framework. We just need to pick the required components to launch the tests. This cures the root cause of slow tests.
Here is a working demo to illustrate this idea.
git clone https://github.com/yangchenyun/lite_spec_helper_demo
It contains the source and specs of a Rails model validator that inherits from ActiveModel::Validators
.
The folder keeps the conventional Rails structure, but omits all code except two relevant files: the validator to be tested and the specs for the validator.
![Screenshot 2013-12-17 09.17.50](https://uploads.sitepoint.com/wp-content/uploads/2013/12/Screenshot-2013-12-17-09.17.50.png)
Running rspec spec/models
, you will find it blazingly runs three tests. This is the minimal requirements to launch a test.
$ rspec spec/
...
Finished in 0.02124 seconds
3 examples, 0 failures
Learn to Run Tests Without Rails
A quick glimpse over the this spec file will reveal several lines which usually don’t appear in such files:
# ...
require 'active_model'
require 'active_support/core_ext/object'
require_relative '../../app/models/order_delivery_date_validator'
# ...
The two require
s in the above snippet are dependencies required to run the validator within the spec. This extra work is necessary because we are not loading Rails and all its magic under the hood.
Firstly, no components of Rails are loaded by default – no ActiveModel
, no ActiveSupport
, no ActionController
, nor is any of our project code. Secondly, ActiveSupport::Autoload
is not utilized here, so we cannot rely on Rails to guess object names and load the correct source file. The responsibility to load required files is ours now.
require 'active_model'
loads in this file and makes ActiveModel::Validator
available.
require 'active_support/core_ext'
extends Object
with blank?
and Fixnum
with days
.
This manual work also brings another implicit benefit. The external APIs are exposed and provide the infomation about the context in which our object lives.
Finally, require_relative '../../app/models/order_delivery_date_validator'
loads the source code to be tested through a relative path.
Build the Minimal spec_helper_lite.rb
Now, we start subtracting what is common across multiple similar specs.
The require_relative
line is a good point to start. The ../..
is too verbose and makes it hard to maintain if we change folder structure. What if we could require the source code back in a more concise way such as require 'order_delivery_date_validator'
?
This can be achieved by modifying the $:
($LOAD_PATH
) in Ruby. When we require 'something'
, Ruby will look in all the paths in $:
and look for something.rb
to load according to the ruby-doc:
If the filename does not resolve to an absolute path, it will be searched for in the directories listed in $LOAD_PATH ($:). If the filename has the extension “.rbâ€, it is loaded as a source file …
To enable this concise syntax, we simply add the app/models
directory to $:
. We can also add any directories that hold other source using this configuration.
# spec/spec_helper_lite.rb
# ...
$:.unshift File.expand_path '../../app/models', __FILE__
Now we can utilize this light helper in our specs. Instead of using require_relative
with a long path with lots of ..
, we could just require 'order_delivery_date_validator'
. Furthermore, we can always put commonly required files for most specs into this “lite” helper in the future.
Here is the updated repo with this newly created spec_helper_lite.rb
.
Less is More
Compared to the feature-rich typical spec_helper.rb
, our spec_helper_lite.rb
is minimal. Instead of loading all the framework overhead, it loads only the test framework rspec
and tweaks $:
to ease the load of our project source code.
This spec_helper.rb
supports unit testing well and is much faster than the original one. Also, it reveals the dependencies in the specs and detects over-complex objects, as well.
The full-functioning spec_helper.rb
could still be loaded for integration tests and acceptance tests, but the spec_helper_lite.rb
will make you happier in your frequently running unit tests.
Conclusion
From a minimal requirement to run unit tests against an object in Rails, we’ve created lite version of spec_helper.rb
. It provides minimal utilities for file loading and reveals object dependencies, all while speeding up unit tests.
Frequently Asked Questions (FAQs) about Lite Spec Helper for Faster Rails Tests
What is the main difference between RSpec’s spec_helper and Rails’ helper?
The spec_helper file in RSpec is a minimal configuration file that doesn’t depend on Rails. It’s used for specs which don’t need to interact with Rails (like specs for classes in the lib directory). On the other hand, Rails’ helper is loaded with Rails-specific configuration and is used for specs that need to interact with Rails, like model specs, controller specs, etc.
How does Lite Spec Helper make Rails tests faster?
Lite Spec Helper makes Rails tests faster by reducing the load time of the test suite. It does this by only loading the components that are necessary for the test to run. This means that unnecessary components, such as middleware and routing, are not loaded, resulting in a faster test suite.
How do I implement Lite Spec Helper in my Rails application?
To implement Lite Spec Helper in your Rails application, you need to create a new file in the spec directory called lite_spec_helper.rb. In this file, you should require the necessary components for your test. Then, in your spec file, instead of requiring ‘rails_helper’, you should require ‘lite_spec_helper’.
Can I use Lite Spec Helper for all my Rails tests?
While Lite Spec Helper can make your tests run faster, it’s not suitable for all types of tests. It’s best used for unit tests that don’t interact with Rails. For tests that do interact with Rails, such as controller or feature tests, you should still use rails_helper.
What are the potential drawbacks of using Lite Spec Helper?
The main drawback of using Lite Spec Helper is that it requires more configuration. You need to manually require the components that your test needs. This can make your tests more complex and harder to maintain. Additionally, if you forget to require a necessary component, your test may fail.
How does Lite Spec Helper compare to other methods of speeding up Rails tests?
Lite Spec Helper is a simple and effective way to speed up Rails tests. However, there are other methods you can use in conjunction with Lite Spec Helper to further improve your test speed, such as parallel testing and test profiling.
Can I use Lite Spec Helper with other testing frameworks?
Lite Spec Helper is specifically designed for use with RSpec. If you’re using a different testing framework, you’ll need to find a similar solution that’s compatible with that framework.
How can I determine if Lite Spec Helper is right for my Rails application?
If your Rails application has a large number of unit tests that don’t interact with Rails, and your test suite is slow, then Lite Spec Helper could be a good solution for you. However, if your application has a lot of tests that do interact with Rails, or if your test suite is already fast, then Lite Spec Helper may not provide much benefit.
What is the impact of Lite Spec Helper on test accuracy?
Lite Spec Helper does not impact the accuracy of your tests. It simply reduces the load time of your test suite by only loading the necessary components. Your tests will still run and produce the same results as they would without Lite Spec Helper.
Where can I find more information about Lite Spec Helper?
For more information about Lite Spec Helper, you can refer to the official RSpec documentation. You can also find helpful articles and tutorials online that provide more detailed explanations and examples of how to use Lite Spec Helper.