I have discovered a wonderful gem, Cypress On Rails, that eases a lot of the process of E2E with Rails App. This incredible gem deserves an article.
Hence through this article, we will navigate through the installation and the basics of the uses of Cypress On Rails.
Let's begin with Cypress On Rails
Let's start a new Rails app and test all this. This will be a simple app where we will store artists.
You can use -T
to skip the installation of the default testing gem minitest
.
rails new artists_on_rails -T
We can install the FactoryBot, Rspec, and Cypress-On-Rails gems in the dev and test environments.
group :development, :test do
gem "factory_bot_rails"
gem "rspec-rails"
gem "cypress-on-rails"
end
Then we need to install the gem and trigger the boilerplate code generation.
bundle install
//After a while
rails g rspec:install
rails g cypress_on_rails:install
This will create a folder name Cypress with the following structure :
cypress/
├── app_commands
│ ├── activerecord_fixtures.rb
│ ├── clean.rb
│ ├── eval.rb
│ ├── factory_bot.rb
│ ├── log_fail.rb
│ └── scenarios
│ └── basic.rb
├── cypress_helper.rb
├── e2e
│ └── rails_examples
│ ├── advance_factory_bot.cy.js
│ ├── other.cy.js
│ ├── using_factory_bot.cy.js
│ ├── using_fixtures.cy.js
│ ├── using_scenarios.cy.js
│ └── using_vcr.cy.js
└── support
├── commands.js
├── index.js
└── on-rails.js
Six directories, 17 files
Injecting a middleware
When you run the installation of cypress-on-rails a new middleware is injected.
Then every request to your app will pass through this middleware before going to your Application.
If this request comes from Cypress, the middleware will not forward it to your app, and the cypress-on-rails middleware will treat it directly.
rake middleware
use ActionDispatch::HostAuthorization
... a bunch of stuff
use CypressOnRails::Middleware
run ArtOnRails::Application.routes
When you execute some Cypress commands, this will go to a particular endpoint with the prefix /__cypress__/command
.
Cypress.Commands.add('appCommands', function (body) {
Object.keys(body).forEach(key => body[key] === undefined ? delete body[key] : {});
const log = Cypress.log({ name: "APP", message: body, autoEnd: false })
return cy.request({
method: 'POST',
url: "/__cypress__/command",
body: JSON.stringify(body),
log: false,
failOnStatusCode: false
}).then((response) => {
log.end();
if (response.status !== 201) {
expect(response.body.message).to.equal('')
expect(response.status).to.be.equal(201)
}
return response.body
});
});
Then the middleware will intercept the request if it contains this url prefix. This will not be forwarded to the app source.
You can see this in this sample of the code of the lib :
def call(env)
request = Rack::Request.new(env)
if request.path.start_with?("#{configuration.api_prefix}/__e2e__/command")
configuration.tagged_logged { handle_command(request) }
elsif request.path.start_with?("#{configuration.api_prefix}/__cypress__/command")
configuration.tagged_logged { handle_command(request) }
warn "/__cypress__/command is deprecated. Please use the install generator to use /__e2e__/command instead."
else
@app.call(env)
end
end
Seeding
Your E2E tests should not depend on your application's previous state. Then when creating E2E tests, you must populate your database for your specific test case. For example, you can create a rake task to populate your database.
But with Cypress on Rails, you can be much more efficient and develop seeds with Factory Bot. These seeds are called scenarios
in CypressOnRails terminology.
Let's generate the model for the artists. Since we have installed the factory bot gem, this will create the factory for our model.
rails g model artists pseudo:string
Now we can modify the basic.rb
scenario. And use our factory of artists.
/cypress/appCommands/scenarios/basic.rb
FactoryBot.create(:artist)
Cultural Aside
Jean-Michel Basquiat is a famous artist. He began his career under the pseudo of SAMO with graffiti in the street. And then, he had an incredible career; he was friends with Warhol, even though he died young (at 27).
Populate your database
You can now use the scenarios you have defined in your Cypress test. For example, with the basic seeds, we've modified the following:
describe('Rails using factory bot examples', function() {
beforeEach(() => {
//This clean the database
cy.app('clean')
cy.appScenario('basic')
})
})
You use the new cy.appScenario
function, and that's it; when you run your test, the test database will be seeded with your scenario data.
This function is defined in cypress/support/on-rails.js
:
Cypress.Commands.add('appScenario', function (name, options = {}) {
return cy.app('scenarios/' + name, options)
});
This will be based on the previous URL we've seen, /__cypress__/commands
. And add /scenarios/basic
to it here in our case. This will trigger our seed,
Call the factory directly in your test
You can call a factory directly from your Cypress test when you need to create specific data for a test.
You will use the function cy.appFactories
. It takes an array of an array with:
-The method you want to call on the factory (create
)
-The factory of the model you want to call (artist
)
-The value that we want to provide to the factory ({pseudo:'Jonone'}
)
it('using single factory bot', function() {
cy.appFactories([
['create', 'artist', {pseudo: 'JonOne'} ]
])
})
With this, you will call the factory directly. The database will be populated with the data you provided to the appFactories method.
Cultural Aside
JonOne is a famous artist. He began his career with graffiti in New York and then came to Paris. Now he is mainstream; he even collaborates with one of the most prominent French retail brands for a marketing campaign.
Conclusion
When you have a stack combining Cypress and Rails, Cypress On Rails is a valuable gem that can ease your E2E test seeding.
Thanks to this gem, you can create seeds called scenarios in Cypress On Rails terminology. These scenarios can use Factory Bot directly, making seeding easier when you have dependencies (not treated in this article).
(PS): TIL that plural of scenario, an Italian word, is scenarii :).
Top comments (2)
Nice article!! I myself am learning Cypress for a small project I'm involved with at work. Though it's not in rails, but still I can easily use the concepts demonstrated here.
Thanks
Happy it helps ! :D