When we first started evaluation what to use for automation at work, we started off with this idea of using Cucumber. In a previous post I briefly talked about Cucumber and it’s Gerkin syntax. This syntax can be nice for some, and can be just another layer of maintenance for others. Rather than discuses the pros and cons of using Cucumber, I thought I’d just throw together a post for how to set things up using Capybara and either Cucumber or RSpec. So without further reading let’s begin.
Cucumber
Cucumber is pretty nice for it’s setup. Cucumber really just reads a few ruby files to learn the step definitions and then executes a bunch of features. The most basic folder structure for Cucumber looks like this
1 2 3 4 5 |
. ├──features ├── step_definitions └── support └── env.rb |
When you execute the cucumber command, you do it in the folder containing the features folder. If you want a more complex folder structure, you can look at the documentation here.
The first step to setting up Capybara to use Cucumber is to put some stuff into env.rb. It can be as simple as the following:
1 2 3 4 5 6 7 |
require 'selenium-webdriver' require 'capybara/cucumber' #I don't think you actually need this line require 'rspec/expectations' Capybara.default_driver = :selenium |
You can of course do other things in here to register a different driver, but this is the simplest env.rb I’ve come up with. You load in a few files and tell Capybara to use selenium for the web driver.
With that done, you can now start creating step definitions in the step_definitions folder, and features in the features folder. When you want to run the tests just execute the cucumber command.
If you want to write your step definitions in the interactive ruby prompt, read this post for the basics of getting into interactive mode.
Simple as can be to setup, but I personally find Cucumber to be an extra layer of maintenance that I don’t want to deal with. So I prefer the RSpec setup.
RSpec
This one is a little more complicated when you don’t know much about Ruby. First off, some common practices of Ruby.
I come from a primarily C based background. So I like to writeVariableNamesLikeThis. It’s simple, doesn’t require reaching all over the keyboard, and I just like it. ClassNamesLikeThis variableNamesLikeThis and CONSTANTS_ARE_THE_ONLY_WEIRD_ONES. I don’t know about anyone else, but I can read that without skipping a beat. Ruby however is written with slightly different conventions. Instead we write things_like_this. Which I think is weird, but if you’re going to write Ruby, you’ll see other people do it like that all the time.
RSpec assumes that you’re going to follow the convention by default, so all of your spec files have to be named_something_like_this_spec.rb. A spec file, if you didn’t catch on, is a file that contains tests. And all spec files must end in _spec and have the .rb extension. (This is the default configuration, you can change it, but you’ll have to do that research on your own, sorry)
Once you’ve got that part down, there’s another important part – the spec_helper. The spec_helper has a bunch of setup code, similar to Cucumbers env.rb. I think that env.rb is just a generic Ruby thing and that RSpec should support using one, but honestly this whole Ruby thing is still foreign to me.
Assuming that we just want to use Capybara and Rspec to run UI Automation on a generic web site, we can use the following folder structure
1 2 3 4 5 |
. └──spec ├──features ├── helpers └── spec_helper.rb |
The helpers folder is there because I assume that you’ll want to use some helpers. Helpers is for modules that you want to be common through different features. It’s far smarter than copying and pasting code between feature files.
The features folder is where you will actually put your spec files. Remember the naming convention from above, all spec files must end in _spec.rb
Finally the spec_helper has a few different ways that you can set it up. We work on an application which requires you to log in and do stuff. I know that what I am about to explain will make some QA people cringe, but I honestly don’t care. Because you have to log in to do anything, and I don’t want the test suite to take forever, we manually manage sessions. In our test setup, each test will run right after the other. It does make it so that not every single test starts in the perfect state, and honestly, I like it better that way.
When every single test is essentially
- log in
- navigate to the page that we’re actually testing
- perform some action on the page
- verify the action on the page had the desired results
Then you spend forever doing the first 2 steps a thousand times. There is no reason to assume that if steps 1 and 2 worked the first 500 times that they would fail on 501, and if there is, you should probably re-evaluate some things because you shouldn’t be worried that at any moment everything will come crashing down.
Additionally, people will be far far far too tempted to turn that into something like this
- log in
- navigate to the page that we’re actually testing
- perform some action on the page
- verify the action on the page had the desired results
- goto 3
And if you’re mashing many tests together into 1 big test then the code looks really ugly and is unmaintainable. It’s better to go for something like this
Once per spec:
- reset the session
- log in
- navigate to the page
And then you have many contexts or it block or whatever that do the this
- perform some action on the page
- verify the action on the page had the desired results
- do something to put the page back into the “common” state
Doing this you can speed things up and reduce the amount of duplicate code.
When you do things this way, your spec files will look something like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
require 'spec_helper' feature 'Some awesome feature' do before(:all) do Capybara.reset_session! visit '' login visit '/awesomePage' end context 'validates something' do after :each do get the page back into a common state end it 'tests something' do test something end it 'tests something else' do test something else end end end |
Now that I’ve finished telling all of you why I don’t care that I’m “doing testing wrong”, back to the spec_helper. The spec_helper should be required as the first line of ever spec you write regardless of if you manually control your sessions. This is what the spec helper would look like if you want to manually control your sessions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#these lines are only here if you're using bundler. #I recommend it if you're working on a team require 'rubygems' require 'bundler/setup' require 'rspec' #allows us to do our own sessions. Each feature can create it's own session instead of having to log in for each test require 'capybara' require 'capybara/dsl' require 'capybara/rspec/matchers' require 'capybara/rspec/features' require 'selenium-webdriver' Capybara.default_driver = :selenium Capybara.app_host = 'http://google.com' #this part all came from capybara's rspec file, but I removed the resetting of sessions RSpec.configure do |config| config.include Capybara::DSL, :type => :feature config.include Capybara::RSpecMatchers, :type => :feature # A work-around to support accessing the current example that works in both # RSpec 2 and RSpec 3. fetch_current_example = RSpec.respond_to?(:current_example) ? proc { RSpec.current_example } : proc { |context| context.example } end |
Or if you don’t want to do your own sessions, you can have it slightly simpler
1 2 3 4 5 6 7 8 9 10 11 12 |
require 'rubygems' require 'bundler/setup' require 'capybara/rspec' #this is the line that changes how things work require 'capybara' require 'capybara/dsl' require 'capybara/rspec/matchers' require 'capybara/rspec/features' require 'selenium-webdriver' Capybara.default_driver = :selenium Capybara.app_host = 'http://google.com' |
I know that the RSpec setup looks little more complex, but to me it’s a question of whether you want to spend time on initial setup or on long term maintenance. Cucumber to me has more long term maintenance. Though it also depends on who you expect to be reading and writing your tests. In the end, do what you think will be the most beneficial long term.