rspec-set-0.1.3/0000755000175100017510000000000013626420307012451 5ustar pravipravirspec-set-0.1.3/.gitignore0000644000175100017510000000024313626420307014440 0ustar pravipravi*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp *.sqlite3rspec-set-0.1.3/Rakefile0000644000175100017510000000016513626420307014120 0ustar pravipravirequire "bundler/gem_tasks" require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :default => :spec rspec-set-0.1.3/spec/0000755000175100017510000000000013626420307013403 5ustar pravipravirspec-set-0.1.3/spec/fixtures/0000755000175100017510000000000013626420307015254 5ustar pravipravirspec-set-0.1.3/spec/fixtures/non_active_record_class.rb0000644000175100017510000000026313626420307022452 0ustar pravipraviclass NonActiveRecordClass attr_accessor :name def initialize(attributes) self.name = attributes[:name] end def self.create(attributes) new(attributes) end endrspec-set-0.1.3/spec/fixtures/active_record_class_example.rb0000644000175100017510000000006713626420307023315 0ustar pravipraviclass ActiveRecordClassExample < ActiveRecord::Base endrspec-set-0.1.3/spec/db/0000755000175100017510000000000013626420307013770 5ustar pravipravirspec-set-0.1.3/spec/db/migrate/0000755000175100017510000000000013626420307015420 5ustar pravipravirspec-set-0.1.3/spec/db/migrate/01_create_active_record_class_example.rb0000644000175100017510000000027613626420307025306 0ustar pravipraviclass CreateActiveRecordClassExample < ActiveRecord::Migration def change create_table :active_record_class_examples do |t| t.string :name t.integer :age end end endrspec-set-0.1.3/spec/rspec_set_spec.rb0000644000175100017510000000753113626420307016737 0ustar pravipravirequire '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.rb0000644000175100017510000000272213626420307016224 0ustar pravipravirequire '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 endrspec-set-0.1.3/features/0000755000175100017510000000000013626420307014267 5ustar pravipravirspec-set-0.1.3/features/rspec-set.feature0000644000175100017510000000612313626420307017553 0ustar pravipraviFeature: 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.md0000644000175100017510000000173113626420307014264 0ustar pravipravi# 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/pcreuxrspec-set-0.1.3/lib/0000755000175100017510000000000013626420307013217 5ustar pravipravirspec-set-0.1.3/lib/rspec-set.rb0000644000175100017510000000310313626420307015446 0ustar pravipravirequire "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.rb0000644000175100017510000000007713626420307015235 0ustar pravipravimodule Rspec module RSpecSet VERSION = "0.1.3" end end rspec-set-0.1.3/README.rdoc0000644000175100017510000001010113626420307014250 0ustar pravipravi= rspec-set {Build Status}[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.gemspec0000644000175100017510000000227513626420307015731 0ustar pravipravi# 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/Gemfile0000644000175100017510000000013513626420307013743 0ustar pravipravisource 'https://rubygems.org' # Specify your gem's dependencies in rspec-set.gemspec gemspecrspec-set-0.1.3/.rspec0000644000175100017510000000003613626420307013565 0ustar pravipravi--color --format documentationrspec-set-0.1.3/LICENSE.txt0000644000175100017510000000205713626420307014300 0ustar pravipraviCopyright (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.