rspec-set-0.1.3/ 0000755 0001751 0001751 00000000000 13626420307 012451 5 ustar pravi pravi rspec-set-0.1.3/.gitignore 0000644 0001751 0001751 00000000243 13626420307 014440 0 ustar pravi pravi *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
*.sqlite3 rspec-set-0.1.3/Rakefile 0000644 0001751 0001751 00000000165 13626420307 014120 0 ustar pravi pravi require "bundler/gem_tasks"
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
rspec-set-0.1.3/spec/ 0000755 0001751 0001751 00000000000 13626420307 013403 5 ustar pravi pravi rspec-set-0.1.3/spec/fixtures/ 0000755 0001751 0001751 00000000000 13626420307 015254 5 ustar pravi pravi rspec-set-0.1.3/spec/fixtures/non_active_record_class.rb 0000644 0001751 0001751 00000000263 13626420307 022452 0 ustar pravi pravi class NonActiveRecordClass
attr_accessor :name
def initialize(attributes)
self.name = attributes[:name]
end
def self.create(attributes)
new(attributes)
end
end rspec-set-0.1.3/spec/fixtures/active_record_class_example.rb 0000644 0001751 0001751 00000000067 13626420307 023315 0 ustar pravi pravi class ActiveRecordClassExample < ActiveRecord::Base
end rspec-set-0.1.3/spec/db/ 0000755 0001751 0001751 00000000000 13626420307 013770 5 ustar pravi pravi rspec-set-0.1.3/spec/db/migrate/ 0000755 0001751 0001751 00000000000 13626420307 015420 5 ustar pravi pravi rspec-set-0.1.3/spec/db/migrate/01_create_active_record_class_example.rb 0000644 0001751 0001751 00000000276 13626420307 025306 0 ustar pravi pravi class CreateActiveRecordClassExample < ActiveRecord::Migration
def change
create_table :active_record_class_examples do |t|
t.string :name
t.integer :age
end
end
end rspec-set-0.1.3/spec/rspec_set_spec.rb 0000644 0001751 0001751 00000007531 13626420307 016737 0 ustar pravi pravi require 'spec_helper'
describe 'including Set' do
it 'adds the ::set method to RSpec::Core::ExampleGroup' do
expect(RSpec::Core::ExampleGroup).to respond_to(:set)
end
end
describe 'without an ActiveRecord model' do
setup_for_error_checking(NonActiveRecordClass)
it "warns the user that Set only works with AR models" do
$stderr.rewind
expect($stderr.string.chomp).to eq(
"my_object is a NonActiveRecordClass - rspec-set works with ActiveRecord models only"
)
end
end
describe 'with an ActiveRecord model' do
setup_for_error_checking(ActiveRecordClassExample)
it "doesn't give a warning to the user" do
$stderr.rewind
expect($stderr.string.chomp).to be_empty
end
it 'creates a method based on the argument to ::set' do
expect(self).to respond_to(:my_object)
end
end
describe 'with a destroyed ActiveRecord model' do
set(:my_destroyed_object) do
ActiveRecordClassExample.create(name: 'Alfred', age: 77)
end
it 'allows us to destroy a model' do
my_destroyed_object.destroy
expect(
ActiveRecordClassExample.find_by(id: my_destroyed_object.id)
).to be_nil
end
it 'reloads a destroyed model' do
expect(my_destroyed_object.reload.name).to eq('Alfred')
end
end
describe 'with a stale model' do
set(:my_stale_object) do
ActiveRecordClassExample.create(name: 'Old Name', age: 18)
end
it 'allows us to play with the model' do
my_stale_object.update(name: 'New Name')
expect(ActiveRecordClassExample.find(my_stale_object.id).name).to eq(
'New Name'
)
end
it 'reloads the stale model' do
expect(my_stale_object.name).to eq('Old Name')
end
end
describe ActiveRecordClassExample do
set(:ar_class_example) { ActiveRecordClassExample.create(name: 'ex_1') }
subject { ar_class_example }
context "when name is changed to 'ex_2" do
before do
ar_class_example.update(name: 'ex_2')
end
it 'updates the name' do
expect(subject.name).to eq('ex_2')
end
end
context "when name is 'ex_1" do
it 'reloads the original name' do
expect(subject.name).to eq('ex_1')
end
end
end
describe 'sub sub contexts' do
set(:ar_class_example) { ActiveRecordClassExample.create(name: 'apple') }
subject { ar_class_example }
context "when name is changed to 'banana'" do
before do
ar_class_example.update(name: 'banana')
end
it 'updates the name' do
expect(subject.name).to eq('banana')
end
context "when we append ' is good'" do
before do
ar_class_example.name << ' is good'
ar_class_example.save
end
it 'updates the appended name' do
expect(subject.name).to eq('banana is good')
end
end
context "when we append ' is bad'" do
before do
ar_class_example.name << ' is bad'
ar_class_example.save
end
it 'also updates the appended name' do
expect(subject.name).to eq('banana is bad')
end
context "when we append ' for you'" do
before do
ar_class_example.name << ' for you'
ar_class_example.save
end
it 'contains the full sentence' do
expect(subject.name).to eq('banana is bad for you')
end
end
end
end
context "when name is 'apple'" do
it 'reloads the original name' do
expect(subject.name).to eq('apple')
end
end
end
describe 'deleting an object' do
set(:ar_class_example) { ActiveRecordClassExample.create(name: 'apple') }
subject { ar_class_example }
context "when I destroy the ar_class_example" do
before do
ar_class_example.destroy
end
it "is destroyed" do
expect(ActiveRecordClassExample.find_by_id(ar_class_example.id)).to be_nil
end
end
context "when name is 'apple'" do
it 'is reloaded from the database' do
expect(subject.name).to eq('apple')
end
end
end
rspec-set-0.1.3/spec/spec_helper.rb 0000644 0001751 0001751 00000002722 13626420307 016224 0 ustar pravi pravi require 'rspec-set'
require 'database_cleaner'
require 'active_record'
require_relative './fixtures/non_active_record_class'
require_relative './fixtures/active_record_class_example'
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
config.before(:suite) do
setup_database
end
config.after(:suite) do
destroy_database
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.order = 'random'
end
def db
ActiveRecord::Base.connection
end
def setup_database
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'spec/db/rspec-set-test.sqlite3'
)
db.tables.each do |table|
db.execute("DROP TABLE #{table}")
end
Dir[File.join(File.dirname(__FILE__), "db/migrate", "*.rb")].each do |f|
require f
migration = Kernel.const_get(f.split("/").last.split(".rb").first.gsub(/\d+/, "").split("_").collect{|w| w.strip.capitalize}.join())
migration.migrate(:up)
end
end
def destroy_database
db.tables.each do |table|
db.execute("DROP TABLE #{table}")
end
end
def setup_for_error_checking(klass)
before do
@orig_stderr = $stderr
$stderr = StringIO.new
end
set(:my_object) { klass.create(name: 'Test Name') }
after do
$stderr = @orig_stderr
end
end rspec-set-0.1.3/features/ 0000755 0001751 0001751 00000000000 13626420307 014267 5 ustar pravi pravi rspec-set-0.1.3/features/rspec-set.feature 0000644 0001751 0001751 00000006123 13626420307 017553 0 ustar pravi pravi Feature: set
As a rails developer willing to speed-up my integration tests
In order to take advantage of examples running in transactions
I want to use #set that store the data before all example and
reload active record objects before each examples
Scenario: Examples run in transactions (no side effects between examples)
Given a file named "spec/models/widget_spec.rb" with:
"""
require "spec_helper"
describe Widget do
set(:widget) { Widget.create!(:name => 'widget_1') }
subject { widget }
context "when name is changed to 'widget_2" do
before do
widget.update_attributes!(:name => 'widget_2')
end
its(:name) { should == 'widget_2' }
end
context "when name is 'widget_1" do
its(:name) { should == 'widget_1' }
end
end
"""
When I run "rspec spec/models/widget_spec.rb"
Then the examples should all pass
Scenario: We can use sub sub contexts just fine
Given a file named "spec/models/widget_spec.rb" with:
"""
require "spec_helper"
describe Widget do
set(:widget) { Widget.create(:name => 'apple') }
subject { widget }
context "when name is changed to 'banana" do
before do
widget.update_attributes!(:name => 'banana')
end
its(:name) { should == 'banana' }
context "when we append ' is good'" do
before do
widget.name << ' is good'
widget.save!
end
its(:name) { should == 'banana is good' }
end
context "when we append ' is bad'" do
before do
widget.name << ' is bad'
widget.save!
end
its(:name) { should == 'banana is bad' }
context "when we append ' for you'" do
before do
widget.name << ' for you'
widget.save!
end
its(:name) { should == 'banana is bad for you' }
end
end
end
context "when name is 'apple" do
its(:name) { should == 'apple' }
end
end
"""
When I run "rspec spec/models/widget_spec.rb"
Then the examples should all pass
Scenario: I can delete an object, it will be available in the next example.
Given a file named "spec/models/widget_spec.rb" with:
"""
require "spec_helper"
describe Widget do
set(:widget) { Widget.create(:name => 'apple') }
subject { widget }
context "when I destroy the widget" do
before do
widget.destroy
end
it "should be destroyed" do
Widget.find_by_id(widget.id).should be_nil
end
end
context "when name is 'apple" do
its(:name) { should == 'apple' }
end
end
"""
When I run "rspec spec/models/widget_spec.rb"
Then the examples should all pass
Scenario: I can update a model in a before block
Scenario: I can use a set model in another set definition
rspec-set-0.1.3/CHANGELOG.md 0000644 0001751 0001751 00000001731 13626420307 014264 0 ustar pravi pravi # rspec-set CHANGELOG
## 0.1.3
* prevent name conflict by [@jdelStrother][] (PR [#6][])
*
## 0.1.2
* fix reload destroy models by [@pcreux][]
## 0.1.1
* add test suite by [@loganhasson][] (PR [#4][])
* fix relig destroyed models by [@loganhasson][] (PR [#5][])
## 0.1.0
* only reload persisted models
* relig destroyed models
* warn when used with non active record objects
* add support for `rspec >= 2.12` by [@21croissants][]
## 0.0.1
* create model in `#before(:all)` statement
* reload model in `#before(:each)` statement
* make model available via `#let()`
[#4]: https://github.com/pcreux/rspec-set/issues/4
[#5]: https://github.com/pcreux/rspec-set/issues/5
[#6]: https://github.com/pcreux/rspec-set/issues/6
[@21croissants]: https://github.com/21croissants
[@jdelStrother]: https://github.com/jdelStrother
[@loganhasson]: https://github.com/loganhasson
[@pcreux]: https://github.com/pcreux rspec-set-0.1.3/lib/ 0000755 0001751 0001751 00000000000 13626420307 013217 5 ustar pravi pravi rspec-set-0.1.3/lib/rspec-set.rb 0000644 0001751 0001751 00000003103 13626420307 015446 0 ustar pravi pravi require "version"
module RSpec
module Core
module RSpecSet
module ClassMethods
# Set @variable_name in a before(:all) block and give access to it
# via let(:variable_name)
#
# Example:
#
# set(:transaction) { Factory(:address) }
#
# it "should be valid" do
# transaction.should be_valid
# end
#
def set(variable_name, &block)
before(:all) do
# Create model
self.class.send(:class_variable_set, "@@__rspec_set_#{variable_name}".to_sym, instance_eval(&block))
end
before(:each) do
model = send(variable_name)
if model.is_a?(ActiveRecord::Base)
if model.destroyed?
# Reset destroyed model
self.class.send(:class_variable_set, "@@__rspec_set_#{variable_name}".to_sym, model.class.find(model.id))
elsif !model.new_record?
# Reload saved model
model.reload
end
else
warn "#{variable_name} is a #{model.class} - rspec-set works with ActiveRecord models only"
end
end
define_method(variable_name) do
self.class.send(:class_variable_get, "@@__rspec_set_#{variable_name}".to_sym)
end
end # set()
end # ClassMethods
def self.included(mod) # :nodoc:
mod.extend ClassMethods
end
end # RSpecSet
class ExampleGroup
include RSpecSet
end # ExampleGroup
end # Core
end # RSpec
rspec-set-0.1.3/lib/version.rb 0000644 0001751 0001751 00000000077 13626420307 015235 0 ustar pravi pravi module Rspec
module RSpecSet
VERSION = "0.1.3"
end
end
rspec-set-0.1.3/README.rdoc 0000644 0001751 0001751 00000010101 13626420307 014250 0 ustar pravi pravi = rspec-set {
}[https://travis-ci.org/pcreux/rspec-set]
#set is a little RSpec helper that speeds-up drastically integration tests that relies on active record objects.
#set takes advantage of the fact that RSpec rails runs each examples in SQL transactions.
Since all the changes made to the database are rolledback after each example we can create an active record object before all examples
and use it in each examples without any collisions as long as we reload the object from the database before each example.
#set can be used as a replacement of #let: #set will create the resource before(:all) your examples and will reload the resource before(:each) example.
You can drastically improve the time spent to run your specs. On an application with 3000 examples we decreased the specs duration by 70%!
== Usage
The following code will create one (and only one!) flight before running the examples and reload the flight from the DB before each example.
require 'spec_helper'
describe Flight do
set(:flight) do
Flight.create!
end
it "should be on_time" do
flight.should be_on_time
end
it "should be cancellable" do
flight.cancel
flight.should be_cancelled
end
it "should be delayable" do
flight.delay
flight.should be_delayed
end
end
=== How does that work?
RSpec wraps each example in an SQL transaction which gets rolled back at the end of each example.
#set creates a flight once before running any example. Each example uses this flight and changes its state. Since RSpec rolls back the SQL transaction, the flight gets back to its initial state before each example. #set takes care of reloading the flight from the DB before each example. Examples won't affect each others then.
You can find more examples in https://github.com/pcreux/rspec-set/blob/master/features/lib/rspec-set.feature
== Notes
* #set works only with ActiveRecord objects saved to the DB so far.
* The record created by #set won't be deleted from the database. I encourage you to use DatabaseCleaner with the :truncation strategy to clean up your database. So far in RSpec 2.0, before(:all) runs before all describe/context blocks while after(:all) runs after every single describe/context/it blocks. Just make sure that you call DatabaseCleaner.clean in a before(:all) or after(:suite) then. :)
* #set does not handle multi-level transactions.
* You will have to call DatabaseCleaner.clean before(:all) specs which rely on having an empty database. #set don't clean the database for you.
== Install
Add rspec-set to you Gemfile
gem 'rspec-set'
and replace calls to #let creating active record objects by #set.
== TODO
* support non saved active record objects (changes made to non saved active record objects won't be rolledback after each example)
* make before(:all) running in a transaction - See: http://rhnh.net/2010/10/06/transactional-before-all-with-rspec-and-datamapper
* support multi-level transactions (combinations of subcontext with set and changes made in before(:all) leads to weird behaviour sometimes
== Contributing to rspec-set
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
* Fork the project
* Start a feature/bugfix branch
* Commit and push until you are happy with your contribution
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
== Copyright
Copyright (c) 2010 Philippe Creux. See LICENSE.txt for
further details.
rspec-set-0.1.3/rspec-set.gemspec 0000644 0001751 0001751 00000002275 13626420307 015731 0 ustar pravi pravi # coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'version'
Gem::Specification.new do |spec|
spec.name = "rspec-set"
spec.version = Rspec::RSpecSet::VERSION
spec.authors = ["Philippe Creux"]
spec.email = ["pcreux@gmail.com"]
spec.description = "#set(), speed-up your specs"
spec.summary = "#set() is a helper for RSpec which setup active record
objects before all tests and restore them to there original state
before each test"
spec.homepage = "http://github.com/pcreux/rspec-set"
spec.license = "MIT"
spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "~> 2.14.1"
spec.add_development_dependency "database_cleaner"
spec.add_development_dependency "activerecord"
spec.add_development_dependency "sqlite3"
end
rspec-set-0.1.3/Gemfile 0000644 0001751 0001751 00000000135 13626420307 013743 0 ustar pravi pravi source 'https://rubygems.org'
# Specify your gem's dependencies in rspec-set.gemspec
gemspec rspec-set-0.1.3/.rspec 0000644 0001751 0001751 00000000036 13626420307 013565 0 ustar pravi pravi --color
--format documentation rspec-set-0.1.3/LICENSE.txt 0000644 0001751 0001751 00000002057 13626420307 014300 0 ustar pravi pravi Copyright (c) 2013 Philippe Creux
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.