Who is there?
When it comes to testing an existing product, and even more about developing something new based on the initial writing of usage scenarios, various specifications and tests, you can often hear such things:
11:24:21 PM Michael: well, i need to try
11:24:24 PM Michael: that's probably better
11:24:27 PM Michael: even I think for sure
11:24:36 PM Michael: but for now, something stops me
11:24:38 PM Michael: probably laziness :)
Familiar? “
Do not want to understand? No time? "Then read on. The article will tell you how to set up your favorite rail environment to develop with the approach of
BDD and start a new life (optional).
Initial data
For the purity of the experiment, let's start from scratch. I hope everyone has been friends with
RVM for a long time (if not,
run to get to know each other). Great for managing
Ruby versions and all related jams. It is put for one team in the terminal window (well, or whatever is not in the macos, the console, probably). Immediately we forget about the privileges of Ruta.
So, create a clean environment:
$ rvm gemset create bdd $ rvm gemset use bdd
All, no jams, clean space. We put the last rails:
')
$ gem install rails --no-ri --no-rdoc ... 23 gems installed
The last two keys are needed to not spend too much time and not to put any unnecessary documentation. I added these keys all the time, so in the end I sent this case to
.gemrc :
$ echo 'gem: --no-ri --no-rdoc' >> ~/.gemrc
Great, there are rails. At the time of writing, we are using version
3.0.3 .
We collect all the jams
Create a clean application:
$ rails new bdd
Immediately open the
Gemfile , the one that is at the root. If it does not open, then apparently the wrong text editor, delete
Textmate - set
MacVim (everyone closed the topic,
then I’m not talking ). Add to the file everything you need for testing. In our case, we will use
Cucumber and
RSpec , as well as a lot of all sorts of goodies, which I will discuss later.
source 'http://rubygems.org' gem 'rails', '3.0.3' gem 'mysql2' group :development, :test do gem 'rspec-rails' gem 'cucumber-rails' end group :test do gem 'capybara' gem 'database_cleaner' gem 'factory_girl_rails' gem 'email_spec' gem 'timecop' gem 'launchy' end
The file was corrected, now we put everything:
$ bundle install ... Your bundle is complete!..
Immediately it would be
nice to fix all installed versions in the
Gemfile , i.e. explicitly prescribe the version as well as the jam
rails (the one that is the very first), but in principle it is not critical to test, besides there is a
Gemfile.lock .
We are friends with the rails
RSpec :
$ rails g rspec:install
We're friends with
Cucumber and the last with
RSpec . Do not forget about
Capybara (yes, and it happens, about it just below):
$ rails g cucumber:install --rspec --capybara
We will bring beauty
$ echo '--colour --format documentation' > .rspec
After that,
RSpec will display information on passing tests in a beautiful format. The same can be done for
Cucumber . Open
config / cucumber.yml and execute
:% s / progress / pretty / g (replace
progress with
pretty ).
Finally, ignore the
test folder (just in case, what if) and other trash:
$ echo 'test' >> .gitignore $ echo '*.swp' >> .gitignore $ echo '.DS_Store' >> .gitignore
We’ll put everything we’ve done in
git (everyone should know Uncle
Linus here too):
$ git init . $ git add . $ git commit -m 'Bare Rails 3.0.3 application with BDD environment'
So, here is already a cappuccino with cinnamon aside, proceed directly to the development based on the approach of
BDD . Here, too, I hope for a general idea of this evil monster (calm, good it). Google to the rescue.
Who is who?
Cucumber
https://github.com/aslakhellesoy/cucumberSo,
Cucumber , a cucumber, is a tool for “
elegant and joyful BDD ”, which can be found on its main page. Yes, joy will be more than enough, especially at first with the habit. About
RSpec , I think the authors could say something like that. In any case, there are a lot of documentation and examples for these friends, which only some
wiki pages on the githaba are worth , so I will not particularly extend it, just a short one. At once I will make a reservation that everything is in the context of the rails, although, of course, neither
RSpec nor
Cucumber are limited to this.
Let's return to
Cucumber . It allows you to describe the behavior of the system from the perspective of an external observer (read as "
customer, end user "). In this case, the description is given in a natural language, no begin end to you. Each option of using the system in a cucumber is called a “
feature ”. All of them are in the
features daddy of the same name with an unpredictable
* .feature file
extension . Each file describes one or several “
scenarios ” (
scenario ) characterizing the feature. Scripts consist of a series of steps announced in the files from the
features / step_definitions / * _ steps.rb folder . There are three types of steps:
Given (something given, some prerequisite),
When (something that happens, some actions of the user) and
Then (result, reaction, response), but about such things it is better to live by examples speak. One of my last features (in NG on TV I heard, “
I am not a wizard, I’m just uchus ... ”, in general, it’s very suitable here, don’t judge strictly):
@javascript @redis
Feature: A user sees who is online
In order to know what the portal is alive
As a regular user
I want to see who is online
Background:
Given the section "Personal profiles" exists
Scenario Outline: who is online?
For nobody lot
And a user exists with name: "<name>"
When the user "<name>" <user action> the portal
And I go to the profile of the user "<name>"
Then I should for my user "<name>"
When I go to the section "Personal profiles"
Then I should for my user "<name>"
Examples:
| name | user action | my action |
| Victoria | visits | see |
| Michael | does not visit | not see |
The keyword "
Feature " precedes the name of the feature, according to the rules it should briefly and clearly describe the user's actions. Three lines below it say why, who and what they want. The "
Background " section optionally sets general steps for all feature scripts. Next come either "
Scenario " or "
Scenario Outline ". The first describes the scenario without parameters, the second with the parameters following the keyword "
Examples " below. Labels are placed through the dog (
@ ), allowing to impose additional conditions on the entire feature or a specific script, as well as to perform a test selectively. In principle, everything is readable. Ideally, it should be so that the customer takes this file, opens it with a simple text editor and reads without straining, understands everything, realizes and confirms: “
It! "
As for the configuration of the cucumber, it is carried out in the generated file
features / support / env.rb. There are added the necessary lines of code to connect all that is needed for testing.
RSpec
https://github.com/rspec/rspecRSpec is also used to describe the external behavior of the system, but is more suitable for digging into its insides. That is, if you need to check that some model behaves adequately or that some kind of samopisnaya library justifies the hopes placed on it, then
RSpec itself . But if you want to check that “the
user, having entered his e-mail in the first window, and the second, the password, saw a message about successful login, ” then this is to
Cucumber . Each
RSpec file in common is called “
specs ” (
spec , from
specification ), located in the
spec folder and ends in
* _spec.rb . Files in the
spec folder are divided into subfolders so that they reflect the rail project structure (
models ,
controllers ,
helpers , etc.). Here is a slightly simulated example:
describe User do it { should ensure_length_of(:email).is_at_least(6).is_at_most(100) } it { should validate_format_of(:email).with('ma1f0rmed emai1 address') } subject { Factory :user } it { should validate_uniqueness_of :email } it { should validate_uniqueness_of :address } end
Here, I feel, it shouldn’t have happened, but it’s not about that, but that the specs look like this.
The
RSpec configuration is concentrated in the file
spec / spec_helper.rb . Similarly,
Cucumber's env.rb here comes with all the auxiliary
require and stuff.
Capybara
https://github.com/jnicklas/capybaraCapybara is a handy thing for automating “
browser-based ” application testing. Judging by the illustrations from Google, this is some kind of guinea pig, but it's not about her. Jam gives a lot of auxiliary methods in the test environment, with the result that you can easily check such things as, for example, clicking on links, filling out forms (input fields, drop-down lists, checkboxes, etc.), the presence of any element on the page. Moreover, when another piece is connected to it with the equally mysterious name
Selenium , it allows you to go through the steps of the test script directly in the browser, in the literal sense. You will see, for example, your favorite
Chrome and the pages will run one after another. As a result, you can debug and
javascript . It was interesting to observe for the first time how it checked the list sorting on the page by dragging its elements by itself.
Selenium is not the only engine with which
Capybara can work, but is, in my opinion, the most convenient, and does not require the installation of anything extra, such as
JRuby .
Selenium is easy enough to ask to check something not only in
Chrome , but also in
Firefox (the default option) and even in a donkey. In the case of
Cucumber, it is enough to open the
env.rb file and add the following code indicating the desired browser (see the list of available documents in the
Selenium documentation):
Capybara.register_driver :selenium do |app| Capybara::Driver::Selenium.new app, :browser => :chrome end
Database cleaner
https://github.com/bmabey/database_cleanerJam for database cleaning before or after tests. Convenient to cover the tracks and start from scratch. It supports different databases and adapters to them. I use it both for cleaning
MySQL +
ActiveRecord , and
mongoDB +
Mongoid .
If this jam is connected in a
gemfile , then when generating the environment,
Cucumber will add it to your
env.rb automatically, something like:
require 'database_cleaner' DatabaseCleaner.strategy = :truncation Before do DatabaseCleaner.clean end
For
RSpec, you can write the following in
spec_helper.rb (understand where):
config.before(:suite) do DatabaseCleaner.strategy = :truncation end config.before(:each) do DatabaseCleaner.clean end
Factory girl rails
https://github.com/thoughtbot/factory_girl_railsOh, and before the girls came! So, this library is used to easily create copies of models (replacement of
fixtures that are native to rails, if someone says something about something). In other words, you need to test something with an example. We need both User (
User ), and Article (
Article ), and projects (
Project ) with tasks (
Task ). Do not create objects every time with filling in all required fields. It is here that similar factories come to the rescue. It is enough to define a pattern once and then generate new entities based on it. In conjunction with
RSpec, factories are usually stored in the
spec / factories folder or directly in
spec / factories.rb . For example, like this:
Factory.define :user do |factory| factory.sequence(:email) { |i| "user#{ i }@example.org" } factory.password 'password' factory.password_confirmation { |user| user.password } factory.confirmed_at Time.now end
Everything, now there is enough
Factory (: user) in the tests and we have a lukewarm valid user.
Email Spec
https://github.com/bmabey/email-specYou probably won't tell the name right away, but jam is used to test sending mail. Of course, it fits natively in both
Cucumber and
RSpec . In
env.rb we write:
require 'email_spec'
In spec_helper.rb:
require "email_spec"
Further:
$ rails g email_spec:steps
Add auxiliary ready-made steps for tests.
Timecop
https://github.com/jtrupiano/timecopThis one I especially liked, allows you to travel through time. At first, he used something samopisnoe, and then
came across this cop. It is irreplaceable when you need to check something tied up on time, for example, “
having put a pie in the oven, after waiting four hours, we get embers ”. Check out the article about him (in the list of references), there are steps for
Cucumber . The only thing I would like to note is that over time it is better to mark tests with a special label (yes, those tags, both in
Cucumber and
RSpec ), for example,
@travel_through_time in order to always return after them now, but then the tools will go crazy. I have the following lines in
env.rb :
After('@travel_through_time') do Timecop.return end
The lines tell the cucumber to execute the code after each script with a corresponding label. In
RSpec, you can do something similar.
Launchy
https://github.com/copiousfreetime/launchySometimes it happens that the test falls (sometimes the same), and it is difficult to determine why. I would like to look at the page where, for example,
Cucumber did not find the button “
Create ”. For these purposes, this jam is placed and in the steps of a cucumber it is added unevenly like:
Then 'WTF?' do save_and_open_page end
Then simply in the script, we call the corresponding step before the one that does not pass, and we see in our browser a default saved page with the login form and the inscription “
You do not have permissions to perform this operation ”, something like that.
At the boot:% s / buggy / key / g
The environment is configured and ready to mock yourself to the fullest. Of course, there are no guarantees after this, that it will not remain just tuned, but will be actively used. Everything is in our hands, in general, everything, not just writing the letters of the code, all of life, mmm, yes. But, returning to
BDD (I hope at least someone has looked at the conclusion of the decoding), I know by myself that until you force yourself to describe the behavior of the system in advance (and after that it's not bad either), you will not understand how important it is, but the main thing is convenient and useful. Now I don’t even have to open the browser to make sure that everything works, and especially when the rails heal all the brains of poor makosi. After such a development process, there are always features and specs that test some part of the functionality, which are then easy to run:
$ rspec spec $ cucumber
And here it will already become clear that you or your partner ... well done, in one word.
Thank you for your attention, I will write again if the subject touched the delicate strings of someone's quivering soul. Everything, it's time to sleep.
References (as in school)
About
Cucumber :
About
RSpec :
About
Ryan Bates :
About
Timecop :
About
RVM :
About
Git :
// vim: set ft = habrahabr