Developer(s) | David Chelimsky, Myron Marston, Andy Lindeman, Jon Rowe, Paul Casaretto, Sam Phippen, Bradley Schaefer[1] |
---|---|
Initial release | May 18, 2007[2] |
Stable release | 3.3.0 [3]
/ June 12, 2015 |
Operating system | Cross-platform |
Type | Behavior driven development framework / Test tool |
License | MIT License |
Website | rspec |
RSpec is a behavior-driven development (BDD) framework for the Ruby programming language, inspired by JBehave.[4] It contains its own mocking framework that is fully integrated into the framework based upon JMock[5]. The framework can be considered a domain-specific language (DSL) and resembles a natural language specification.
History
editIt started back in 2005, when Steve, got interested by the idea of developing a testing framework based on the behavior, given by Dave Astels[6] and Aslak Hellesøy.[7] This marked the beginning of RSpec. Later in 2006, David Chelimsky[8] lead the team and released a RSpec-Rails framework for Ruby on Rails.
In the May of 2007, the team was able to release RSpec 1.0 which included many vital features that are still in use today. The next stable version RSpec-2, was released in October 2010, after bringing together the efforts of David and Chad. Later in November 2012, Myron Marston was appointed as the lead RSpec maintainer and Andy Lindeman as the lead RSpec-Rails maintainer. RSpec 3[9] was released in June 2014 with many new features such as verifying doubles,[10] composable matchers,[11] a syntax which doesn’t require monkey patching and many other new features.[12]
Installation
editA gem must be added inside the GEMFILE in order to install RSpec[13]:
source 'https://rubygems.org'
gem 'rspec', '~> 3.3.0'
Then run the following command:
gem install rspec # installs RSpec-core, RSpec-expectations and RSpec-mocks
gem install rspec-core # installs RSpec-core only
To install RSpec-rails for versions later to 3.x add to GEMFILE:
# The gem needs to be added to both development and test groups.
group :development, :test do
gem 'rspec-rails', '~> 3.0'
end
Then run:
bundle install
Running Examples
editThe RSpec code is written inside your_file_spec.rb which resides in the spec folder. In order to execute this code use the following command:
rspec spec/your_file_spec.rb # Run only
or we could run:
rake spec # Run all the examples inside the spec folder.
or run:
rake spec:controllers # Run the code examples in spec/controllers
Components
editRSpec-core & RSpec-expectations
editThe structure for a particular behavior of the instance of a class is specified by RSpec-core and the expected output is specified by RSpec-expectation. The following basic structure is followed by an RSpec code:[13][14]
- The
ExampleGroup
class representing a group of examples that could be executed is declared withdescribe()
method. - The
it()
method declares an example and inside thedescribe
method any number ofit()
methods could be used to specify that number of examples. - The
expect()
method is used to compare the desired and obtained output of executing the example and hence it determines the pass/fail result of that example. - A
context()
block could be specified in order to give some context to the examples mentioned inside this block. describe()
andit()
method comes from Rpsec-core andexpect()
method is from RSpec-expectations.- A
before(:each)
block could be specified inside thedescribe
block such that an initialization of object or a valid state of object going to be used by all examples could be set before each example runs. Abefore(:all)
block could be specified that runs only once before all the examples in an example group run. - Similarly code to be ran after an example completes could be specified in
after()
block.
Consider the following example from a conversation[13]:
"Describe an order."
"It sums the prices of its line items."
Corresponding RSpec code:
#Testing for Order class
rspec.describe Order do
it "sums the prices of its line items" do
order = Order.new
order.add_entry(LineItem.new(:item => Item.new(
:price => Money.new(1.11, :USD)
)))
order.add_entry(LineItem.new(:item => Item.new(
:price => Money.new(2.22, :USD),
:quantity => 2
)))
expect(order.total).to eq(Money.new(5.55, :USD))
end
end
The string passed to the it()
method specifies that the given example is for summing the prices of its items. The expect()
method line suggests that if order.total
is equal to the value returned by Money.new(5.55, :USD)
, then the example passes otherwise it fails with a message like:
expected: #<Money @value=5.55 @currency=:USD>
got: #<Money @value=1.11 @currency=:USD>
Consider another example:
describe User do
context 'with admin privileges' do
before :each do
@admin = Admin.get(1)
end
it 'should exist' do
expect(@admin).not_to be_nil
end
it 'should have a name' do
expect(@admin.name).not_to be_false
end
end
end
In the above RSpec code, "with admin privileges"
specifies a context
for the examples (i.e. should exist
and should have a name
) of the User
class. The before
block is executed before each example such that an instance variable @admin
is initialized for the examples. The expect()
method in the examples checks that the @admin
is not null and also @admin.name
is not false.
RSpec-mocks
editRSpec-mocks provides features like Test double which represent real objects during the test. It also provides fake method implementations and set expectations on messages received by objects.
Consider an example[13]:
# Testing for Statement class
describe Statement do
it "uses the customer's name in the header" do
customer = double('customer')
customer.should_receive(:name).and_return('Aslak')
statement = Statement.new(customer)
statement.generate.should =~ /^Statement for Aslak/
end
end
Here, in the above example, a test-double for customer object is created which represents the object in the test. The test is passed if it verifies the customer name using should_receive()
method and then generate()
method calls for the customer name & customer double returns the name using return()
method.
Test-doubles are used to represent objects that are not been created at that point. But verifying-doubles represent an existing object in the system.
book = instance_double("Book", :pages => 250)
RSpec-rails
editA testing framework for rails 3.x and 4.x. The rails specs reside in the spec/<spec-type>
directory by default. The documentation specifies various rails specs namely: model specs, controller specs, request specs, feature specs, view specs, routing specs and helper specs[13].
Model Specs
editModel specs are used to specify the behavior of a model. The :type=> :mode
is specified for model spec.
Usage Example:
require "rails_helper"
rspec.describe User, :type => :model do
it "orders by last name" do
lindeman = User.create!(first_name: "Andy", last_name: "Lindeman")
chelimsky = User.create!(first_name: "David", last_name: "Chelimsky")
expect(User.ordered_by_last_name).to eq([chelimsky, lindeman])
end
end
The above example[13] is used to test whether the two newly created users are ordered by their last names using ordered_by_last_name
method.
Controller Specs
editController specs are group of specs that determine the behavior of the actions of a particular controller. It does not render the view template by default and can run in complete isolation from model and views but it is necessary that the view template must be present.
require "rails_helper"
rspec.describe PostsController, :type => :controller do
describe "GET #index" do
it "responds successfully with an HTTP 200 status code" do
get :index
expect(response).to be_success
expect(response).to have_http_status(200)
end
it "renders the index template" do
get :index
expect(response).to render_template("index")
end
expect(assigns(:posts)).to match_array([post1, post2])
end
end
end
In the above example[13], the PostsController index
action is checked to see whether the correct HTTP code is received and in another example whether the correct index template is received as response.
Request Specs
editRequest specs are used to define single/multiple request and response cycles. The :type=> :request
is specified for request spec.
require 'rails_helper'
rspec.describe "home page", :type => :request do
it "displays the user's username after successful login" do
user = User.create!(:username => "jdoe", :password => "secret")
get "/login"
assert_select "form.login" do
assert_select "input[name=?]", "username"
assert_select "input[name=?]", "password"
assert_select "input[type=?]", "submit"
end
post "/login", :username => "jdoe", :password => "secret"
assert_select ".header .username", :text => "jdoe"
end
end
The above example[13] tests the entire login process by first creating a user with username and password, then rendering the login page and entering details inside the login form, submitting the form and then checking that the page after logging in contains the user name. assert_select()
method is an assertion that selects elements and performs one or more equality tests.
Routing Specs
editRouting specs are used to test whether a specific route maps to a specific controller and its respective action. A tag :type => :routing
indicates that it is a routing spec.
require 'rails_helper'
rspec.describe "routing to profiles", :type => :routing do
it "routes /profile/:username to profile#show for username" do
expect(:get => "/profiles/jsmith").to route_to(
:controller => "profiles",
:action => "show",
:username => "jsmith")
end
it "does not expose a list of profiles" do
expect(:get => "/profiles").not_to be_routable
end
end
In the above example,[13] the routing spec expects the /profiles/jsmith
request to be routed to the controller action “profiles#show#jsmith”
. The test passes only if that is true.
Upgrade note:
route_for
syntax is replaced with route_to
and be_routable
in the versions after rails 1-x.
View Specs
editView specs are used to provide data to a particular view template and expect certain state regarding the content rendered.
require 'spec_helper'
describe "messages/show.html.erb" do
it "displays the text attribute of the message" do
assign(:message, double("Message", :text => "Hello world!"))
render
rendered.should contain("Hello world!")
end
end
In the above example,[13] text in a message is displayed. This method is tested by creating test double and returning “Hello world!” text to the instance variable @message
. The test is passed when text matches with the argument of contain()
matcher.
Upgrade note:[13]
Used till RSpec-rails - 1.x
assigns[key] = value
render
response.should xyz
Used in versions later to RSpec-rails-2.x
assign(key, value)
render
expect(rendered).to xxx
Matchers
editMatchers[13] like be_a_new, render_template, redirect_to, route_to and be_routable are used to match the object with expected outcome along with the expect()
method.
be_a_new
- It checks if the object is new record.
- This matcher is used mainly in controller specs, though available in all specs.
expect(object).to be_a_new(Widget)
render_template
- It delegates to the command assert_template in Rails.
- It is present in request specs, controller specs and view specs.
In request and controller specs, it is used to render the response object to “new”.
expect(response).to render_template("new")
In view specs, the view object is rendered.
expect(view).to render_template(:partial => "_form", :locals => { :widget => widget } )
redirect_to
- It is present in request specs and controller specs.
- It delegates to the command assert_redirect in Rails.
For example, the redirect_to() matcher is used to test whether the response object is redirected to the correct path.
expect(response).to redirect_to(widgets_path)
Routing matchers like route_to() and be_routable() are used to test whether the object is routed to the correct Rails routing.
route_to
- It is present in routing specs and controller specs.
- It delegates to the command assert_routing in Rails.
expect(:get => "/widgets").to route_to(:controller => "widgets", :action => "index")
be_routable
- This matcher is used to test if a path is recognized by Rails routing.
- This can also be used along with not_to to test routes which should not be routable.
expect(:get => "/widgets/1/edit").not_to be_routable
See also
editReferences
edit- ^ RSpec core team. Retrieved 8 April 2013.
- ^ "All versions of rspec". rubygems.org. Retrieved 14 September 2015.
- ^ "Latest version of Rspec". rubygems.org. Retrieved 14 September 2015.
- ^ "JBehave". JBehave official website. Retrieved 20 September 2015.
- ^ "JMock". JMock Official website. Retrieved 20 September 2015.
- ^ "Dave Astels". GitHub. Retrieved 24 September 2015.
- ^ "Aslak Hellesøy". Retrieved 24 September 2015.
- ^ "David Chelimsky". Retrieved 24 September 2015.
- ^ "Rspec 3". Retrieved 24 September 2015.
- ^ "Verifying Doubles". Retrieved 24 September 2015.
- ^ "Composable Matchers". Retrieved 24 September 2015.
- ^ "New features of RSpec-3.0.0". Retrieved 24 September 2015.
- ^ a b c d e f g h i j k l "Rspec Documentation". Official documentation. Retrieved 24 September 2015.
- ^ "The RSpec Book: Behaviour Driven Development with Rspec, Cucumber, and Friends". Retrieved 24 September 2015.
Further reading
edit- Chelimsky, David; Astels, Dave; Helmkamp, Bryan; North, Dan; Dennis, Zach; Hellesoy, Aslak (December 22, 2010). The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends (First ed.). Pragmatic Bookshelf. p. 448. ISBN 978-1934356371.
External links
edit- Official website
- RSpec
- Beyond Test Driven Development - Behaviour Driven Development. A Google TechTalk by Dave Astels on BDD using RSpec.
- RSpec Best Practices Article by Jared Carroll, retrieved April 24 2011.
- Behavior-driven testing with RSpec Article by Bruce Tate, retrieved July 21 2011.
- Dan North & Associates retrieved September 2015.
- Agile Alliance retrieved 2013.
- Translating TDD to BDD retrieved November 6, 2009.
- Better Specs
Category:Software testing tools Category:Software using the MIT license