How To Build A Web App, part 10 of ?: Setting up Rails testing
This is the tenth in a series of articles taking you through all the actual steps in building a web app. If you’re an aspiring developer, if mucking around with teensy beginner tutorials frustrates you, if you’d love to build a properly substantial app that does fab things, these articles are for you.
Last time, we’d figured out how to poke and prod at Ticketmaster’s Discovery API to make it regurgitate its fabulous comedy data. Today, we’ll begin to poke through that data-laughs firehose, decide which bits we do and don’t want to save in our database, and then save ’em. Let’s get cracking.
We’ll be writing code this time. New Git branch!
$ git checkout -b 3-ticketmaster-api
Same as the first two branches: you can call your own branch whatever you like, but I’m still going with [id]-[name]
. Easier.
So! We’ve got several components to build here. We’ll need to:
Write functions that iterate over all 68-and-counting pages of gigs data produced by Ticketmaster, then load it into Rails’s working memory;
Write functions that parse through this new data, distinguish which gigs/venues/acts already exist in our database and which don’t; create new data, update existing data, and (maybe) delete old, out-of date data;
Write Things called unit tests, which puts all this stuff through its programmatic paces, really thoroughly, so that you can test out everything you’ve just built with a single line of code.
Lots to get through!
Test-driven development
Test-driven development is … wait, no, I’ve charged ahead of myself.
Automated testing
Any decent-sized application has about nineteen million billion functions. Ever got bored and clicked on every single link on Facebook, just to see what happens? Ever wondered just how many different combinations of ways there of using an app as gigantic as Facebook? There are thousands and thousands. It’s insane.
How the hell do you test it all?
Way back in the ’90s, programmers at the time realised you simply can’t just manually test everything, every time you make an update to an app. It’s just too big. You’ll be there all day. Their solution was to instead write these accompanying support-program-doohickeys called unit tests. They automatically put their program through its paces, testing specific little bits of it, or the app as a whole, or anything in between. Depends on the type of test.
Programmers eventually stockpile many thousands of unit tests alongside their apps. The idea is, if you’ve changed your app in some way, you want to be sure there aren’t any horrible side effects. Would you rather (1) just type out a single command and automatically put your app’s code base through these thousands of tests, or (2), spend hours and days manually clicking on everything, typing into everything, doing everything yourself?
Imagine (2). You’ve just written another feature. Test everything again. And now a bug-fix. Test everything again. And another feature. Test everything again. And again. And again. Forever. It’s hell on earth.
Any sane (or non-time-stressed (welcome to my hell, young grasshoppah)) person would pick (1). Yes it’s more work up front, but boy, in any code base bigger than a few lines, it pays for itself fast. Don’t believe me? Work on a really big app yourself that doesn’t have tests, and see what happens. Don’t come cryin’ to me when your precious baby falls over in Production, and weeks of data is lost, and the company president bangs the door down and demands to know what the hell you’re playing at. Seriously. It happens. Her boot will connect with your arse at hypersonic velocity. As you exit the stratosphere, you’ll think “oh whyyy did I not write tests from the very start, why why why, I wish I could go back in time and build things properlyyyy…!”
Wish granted, snookums.
Let’s get cracking. We’ve got more new gems to install. Seriously, Rails testing gems are a vast sprawling universe unto themselves. I’ll take you through a few.
One of Rails’s more popular ones is RSpec. RSpec has its own special lingo. I’ll take you through that. We’ll need to generate random test data, too, with two more gems: FactoryBot (it abstracts away the boring bits of generating database models, I’ll get to that later) and Faker (it generates infinite and hilarious variations of any random data you can think of). We’ll also need a gem called Database Cleaner (makes sure your database has a clean slate for each test).
Yes, I’m throwing many Things here at you all at once. Sorry about that! But that’s testing for you. Lots to take in.
There’s no hurry. If you like, I could type more slowly.
So. Let’s install these testing gems. RSpec and Database Cleaner also require post-install config file mucking-about.
Set up test environment
We’ll need to add rspec-rails
,factory_bot_rails
, faker
, and database_cleaner
to your Gemfile
. But! We’ll need to add them to the right place. If you look in Gemfile
, you’ll see that some gems are just listed any old where, but other gems are inside something called a “group”.
What’s a group, you ask? It’s a collection of gems to be run only when your app is running inside a certain environment.
What’s an environment, you ask? And why am I pulling this label shell-game on you? Apart from it being great fun to tease and torment you, mwa ha ha, there is also method to my madness. Back in article 6, I briefly mentioned environments when talking about databases. The time has now come to explain environments.
They’re basically just different modes of running your app. For example! The default mode for any Rails app is development
. It’s configured to make things easier when you’re constructing your app, writing your code. Every time you save a page, it automatically reloads every class, every Rails code-file. Lots of other fiddly little things too. When you deploy a Rails app to a public web server, with a URL and everything, it’s set to run in production
. It’s optimised for speed.
There’s a third environment, and it’s one we’re setting up now: test
. I’m sure you can guess its purpose. It’s optimised for testing. In the next few articles, I’ll give you some context for how and why.
Now. Groups. We don’t want to load and run every single gem in every single environment. Waste of good processing power. So we divvy up the Gemfile
into groups, to denote which gems we want in which environment. Gems outside groups we run in every environment, and gems inside groups we run only in the labelled environment. Easy peasy.
We want RSpec and Database Cleaner to run only in test
. The other two, Faker and FactoryBot, you might think we’d want running only in test
too, but I’ve found it’s neat, and useful, to play around with them in development mode too. Mainly in the Rails console (I’ll explain the console later).
Edit Gemfile
thusly:
# Gemfilegroup :development, :test do
...
gem 'factory_bot_rails'
gem 'faker'
end...group :test do
gem 'rspec-rails'
gem 'database_cleaner'
end
Then run bundle install
. Installed.
RSpec needs one final fiddly post-install bit. Run rails generate rspec:install
. It adds these files:
$ rails g rspec:install
Running via Spring preloader in process 40357
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
They’re RSpec’s config files. spec_helper.rb
contains settings for RSpec in general; rails_helper.rb
contains Rails-specific settings. RSpec can be used with any Ruby app at all, you see, not just Rails.
Sweet. Now for Database Cleaner. Its job is to ensure a 100% clean slate for the test database from one test to another.
What does a “clean slate” mean? Here’s the deal. Some tests can get really really sophisticated. They hit damn near every part of your code base, they manipulate any type of data you can imagine, across every database table. It’s common to have fifty of these incredibly messy tests in a row, one after another after another, creating and destroying fiendishly complex application state.
Now if you don’t clean up after yourself between tests, then residue-data from past tests will mess up present tests. Bad!
Database Cleaner provides strategies to fight this. It’s a bit like cooks thoroughly washing their dishes, ensuring yesterday’s five-alarm chilli sauce doesn’t flavour today’s crème brûlée.
Here’s how we configure that. You’ll see that rspec:install
created a new folder, rspec
. Inside that, create another folder, rspec/support
. This will contain other support files for other testing gems we’ll need later. You’ll see eventually we’ll need a fair few; this is just to keep them grouped and organised. Inside rspec/support
, create a new file called database_cleaner.rb
. Here’s its content:
# rspec/support/database_cleaner.rbRSpec.configure do |config|
config.before :suite do
DatabaseCleaner.clean_with :truncation
end config.before :each do
DatabaseCleaner.strategy = :transaction
end config.before :each do
DatabaseCleaner.start
end config.after :each do
DatabaseCleaner.clean
end
end
Explaining what all these mean is way beyond the scope of this article. Read this if you’d like to explore more.
To make sure all files inside rspec/support
are loaded, you’ll need to jump into rails_helper.rb
, and on line 23, uncomment
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
It loads every file inside rspec/support
.
And that’s it! You’ve now installed the gems and the background infrastructure required to write Rails tests.
Let’s commit that. Run git add -A .
and git commit -m "Testing gems installed"
.
You can see this commit and its changes, in all its pristine glory, here.
Our Rails test
environment is now set up! Today’s lesson endeth.
Next time: we’ll learn about Ticketmaster’s exact API-response structure, and write our first test for it.