Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on Aug 22nd, 2012  |  syntax: None  |  size: 13.62 KB  |  hits: 8  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. # RSpec 2.0 syntax Cheet Sheet by http://ApproachE.com
  2.  
  3. # defining spec within a module will automatically pick Player::MovieList as a 'subject' (see below)
  4. module Player
  5.         describe MovieList, "with optional description" do
  6.                  
  7.           it "is pending example, so that you can write ones quickly"
  8.          
  9.           it "is already working example that we want to suspend from failing temporarily" do
  10.                 pending("working on another feature that temporarily breaks this one")
  11.                 # actual test code is here, will never be reached
  12.           end
  13.          
  14.           it "is pending when failing" do
  15.                   pending "This will be marked as pending when the block will fail, otherwise (on success) will fail telling 'Why am I pending if I pass?'" do
  16.                           1.should == 2 # will mark example as pending
  17.                           2.should == 2 # will fail asking to remove pending status of example
  18.                   end
  19.           end
  20.          
  21.           # this will automatically generate name of the example based on the expectations inside it ~ 'it' with no description
  22.           specify { [1,2,3].should have(3).items }
  23.          
  24.           #any helper methods, before/after, modules etc declared in the outer group are available in the inner group.
  25.           describe "outer" do
  26.                   before(:each) { puts "first" }
  27.                   describe "inner" do
  28.                         before(:each) { puts "second" }
  29.                         it { puts "third"}
  30.                         after(:each) { puts "fourth" }
  31.                   end
  32.                   after(:each) { puts "fifth" }
  33.           end
  34.  
  35.           # 'describe' and 'context' are equivalent
  36.           # I prefer to use 'context' for defining an 'environment'
  37.           context "when first created" do
  38.                 it "is empty" do
  39.                   movie_list = MovieList.new
  40.                   movie_list.should be_empty
  41.                 end
  42.           end
  43.  
  44.           # I prefer to use 'describe' for nouns, verbs; defining a nested set of specifications
  45.           describe "forward" do
  46.                 it "should jump to a next movie" do
  47.                   next_movie = MovieList.new(2).forward
  48.                   next_movie.track_number.should == 2
  49.                 end
  50.           end
  51.         end
  52.        
  53.         it "will have default subject that corresponds to the instance of first parameter in 'describe'" do
  54.                 subject.class.should be == MovieList
  55.         end
  56.         # unless subject is set explicitly
  57.         subject { MovieList.new(10) } # approximately similar to 'before(:each)'
  58.         # no need to use 'subject.should', use 'should'
  59.         specify { should have(10).items } # same as below
  60.         specify { subject.should have(10).items }
  61.        
  62.  
  63.         # similar to specify { subject.track_number.should == 1}
  64.         its(:track_number) { should == 1 }
  65.  
  66.         context "specs set-up" do
  67.                 # we can run setup before each examle, or all of them
  68.                 before(:each) do               
  69.                         @new_on_each_example = YourObject.new
  70.                 end
  71.                 before do
  72.                         @new_on_each_spec_less_verbose = YourObject.new
  73.                 end
  74.                 before(:all) do
  75.                         # Avoid using it as it will bring the 'shared state' into unit tests
  76.                         @same_instance_for_all_examples_within_the_context = YourObject.new
  77.                 end            
  78.                 it "can access attributes defined in 'before'" do
  79.                         @new_on_each_example.should_not be_nil
  80.                         @same_instance_for_all_examples_within_the_context.should_not be_nil
  81.                 end
  82.                
  83.                 # cleanup code can be run the same way using 'after' instead of 'before'
  84.                 # Avoid using 'after'          
  85.                 # we can wrap examples: before + after + manual handling
  86.                 # In most cases 'before' + 'after' will work better.
  87.                 around do |example|                    
  88.                         DB.transaction { example.run }
  89.                         # should handle errors manually, so do not do something like:
  90.                         # DB.start_transaction
  91.                         # example.run
  92.                         # DB.rollback_transaction
  93.                 end
  94.                 it "should run within a transaction" do
  95.                         MovieList.new.save!
  96.                 end            
  97.                
  98.                 let(:new_on_each_example) { ObjectPerExample.new }
  99.                 it "can use method defined by 'let'" do
  100.                         new_on_each_example.should_not be_nil
  101.                         # the object is memoized, so
  102.                         new_on_each_example.should == new_on_each_example
  103.                 end
  104.                
  105.                 # defining helper methods within context may be more useful than setup
  106.                 def forward(times) do
  107.                         list = MoviewList.new(10)
  108.                         list.forward(times).track_number
  109.                 end
  110.                 it "can use it multiple times" do
  111.                         forward(1).should == 1
  112.                         forward(2).should == 2
  113.                         forward(10).should == 1
  114.                 end
  115.                
  116.                 # using 'yield' with helper methods
  117.                 def given_thing_with(options)
  118.                         yield Thing.new do |thing|
  119.                                 thing.set_status(options[:status])
  120.                         end
  121.                 end
  122.                 it "should do something when ok" do
  123.                         given_thing_with(:status => 'ok') do |thing|
  124.                                 thing.do_fancy_stuff(1, true, :move => 'left', :obstacles => nil)
  125.                         end
  126.                 end
  127.                
  128.                
  129.                 # helpers can come from modules
  130.                 module Helpers
  131.                         def shared_help
  132.                                 [1,2,3]
  133.                         end
  134.                 end
  135.                 include Helpers
  136.                 it "can use helpers from Module" do
  137.                         shared_help.should == [1,2,3]
  138.                 end
  139.                 # or this module can be included for ALL example groups automatically during configuration:
  140.                 # RSpec.configure do |config|
  141.                 #       config.include Helpers
  142.                 # end
  143.         end
  144.        
  145.        
  146.         context "built-it stubbing, faking, mocking" do
  147.                 it "can stub" do
  148.                         source = double('source')                      
  149.                         source.stub(:fetch) { [1,2,3,4,5] }                    
  150.                         source.stub(:fetch_from).and_return([1,2]) # other way                 
  151.                         MovieList.stub(:find).and_return(MovieList.new) # stub class method
  152.                        
  153.                         implementing = double('source')
  154.                         implementing.stub(:fetch) do |count|
  155.                                 count == 5 ? [1,2,3] : [4,5,6,7] # provide stub logic here, easy to use for Fakes
  156.                         end                    
  157.                         # easily stub chains of calls
  158.                         Blog.stub_chain(:posts, :published, :recent).and_return([1,2,3])
  159.                         Blog.posts.published.recent.should == [1,2,3]
  160.                 end
  161.                 it "can ignore non-expected method calls (NullObject pattern)" do
  162.                         source = double('source', :url => 'http://example.com').as_null_object                 
  163.                         source.any_method_call_onwill_return_nil.should be_nil
  164.                         # the source mock object will record the 'any_method_call_onwill_return_nil' message internally though                 
  165.                 end
  166.                 it "can set expectations" do
  167.                         source = double('source')
  168.                         # arguments
  169.                         source.should_receive(:fetch).with(10, "abc").and_return([1,2]) # expecting arguments (10, "abc") otherwise failing
  170.                         source.should_receive(:fetch).with(instance_of(Integer), "abc").and_return([1,2]) # don't care about 1st argument as long as it is Integer
  171.                         source.should_receive(:fetch).with(10, anything).and_return([1,2]) # don't care about 2nd argument at all
  172.                         source.should_receive(:fetch).with(any_args) # same as not using 'with' - don't care about arguments
  173.                         source.should_receive(:fetch).with(no_args) # 0 arguments, otherwise fail
  174.                         source.should_receive(:fetch).with(hash_including(:count => 10, :url => 'abc')) # arg should be Hash with all the values mentioned
  175.                         source.should_receive(:fetch).with(hash_not_including(:timeout => 5)) # arg should be Hash that contains no ':timout=>5'
  176.                         source.should_receive(:fetch).with(anything, /example/) # 2nd arg shuold match RegEx
  177.                         source.should_receive(:fetch).and_return([1], [1,2], [1,2,3]) # 1st call - [1], 2nd - [1,2], 3rd - [1,2,3], 4th - [1] and so on ...
  178.                         # expectation overrides stub
  179.                         source.stub(:fetch).and_return([1,2]) # will return [1,2] when called
  180.                         source.should_recieve(:fetch).and_return([3,4]) # prev been overriden and will return [3,4]
  181.                         # raising/throwing
  182.                         source.should_receive(:fetch).and_raise # raise Exception
  183.                         source.should_receive(:fetch).and_raise(ZeroDivisionError) # raise ZeroDivisionError
  184.                         source.should_receive(:fetch).and_raise(Exception.new('instance of aexception')) # raise given exception
  185.                         source.should_receive(:fetch).and_throw(:zero) # thro :zero                                    
  186.                         # order
  187.                         source.should_receive(:first).ordered # order matters in relation to others marked as ordered
  188.                         source.should_receive(:dosnt_matter) # don't care about order as long as it is called
  189.                         source.should_receive(:second).ordered # must be called after 'first'
  190.                         # order is not enforced across different objects:
  191.                         double('a').should_receive(:a).ordered # not related to the next one
  192.                         double('b').should_receive(:b).ordered # not related to the prev one
  193.                         # how many times?
  194.                         source.should_recieve(:fetch).exactly(1)times
  195.                         source.should_recieve(:fetch).at_most(5)times
  196.                         source.should_recieve(:fetch).at_least(2)times
  197.                         source.should_recieve(:fetch).twice
  198.                         source.should_recieve(:fetch).once
  199.                         # negative expectations
  200.                         source.should_recieve(:fetch).never
  201.                         source.should_recieve(:fetch).exactly(0)times
  202.                         source.should_not_recieve(:fetch)                      
  203.                        
  204.                        
  205.                         list = MovieList.new(source)
  206.                         # if source.fetch has not been called, then example will fail
  207.                 end
  208.                
  209.                 context "custom expectations" do
  210.                         # define custom expection class somewhere
  211.                         class GreaterThanMatcher
  212.                                 def initialize(expected)
  213.                                         @expected = expected
  214.                                 end
  215.                                 def description
  216.                                         # will generate proper failure message and name of the example
  217.                                         "a number greater than #{@expected}"
  218.                                 end
  219.                                 def ==(actual)
  220.                                         # this will be called from
  221.                                         actual > @expected
  222.                                 end
  223.                         end
  224.                        
  225.                         # add this method to the RSpec (see set-up for global configuration)
  226.                         def greater_than(floor)
  227.                                 GreaterThanMatcher.new(floor)
  228.                         end
  229.                        
  230.                         it "can be used in expectations" do
  231.                                 subject.should_recieve(:forward).with(greater_than 3)
  232.                                 subject.forward(5)
  233.                         end
  234.                 end # custom matchers
  235.         end # mocking
  236.        
  237.  
  238.        
  239.        
  240.        
  241.         # set of same examples shared accross multiple specs
  242.         # shared_examples_for should be in a separate file and defined outside of 'describe'/'context'
  243.         shared_examples_for "any pizza" do
  244.                 it "tastes really good" do
  245.                         @pizza.should taste_really_good
  246.                 end
  247.         end
  248.        
  249.         # to include the shared examples, into example groups:
  250.         # it will assume @pizza instance variable is available here
  251.         it_behaves_like "any pizza"
  252.        
  253.         context 'defining examples dynamically - everybody knows that :)' do
  254.                 {2 => 4, 3 => 6, 10 => 20}.each do |input, output|
  255.                         it "#{input} * 2 should be equal to #{output}" do
  256.                                 (input * 2).should == output
  257.                                 # will produce examples:
  258.                                 # - 2 * 2 should be equal to 4
  259.                                 # - 3 * 2 should be equal to 6
  260.                                 # - 10 * 2 should be equal to 20
  261.                         end
  262.                 end
  263.         end
  264.        
  265.        
  266.         context "matchers" do
  267.                 it "shows built-in matchers" do
  268.                         # TODO: describe ===, eql, equal
  269.                         1.should == 1
  270.                         1.should_not == 2 # NOT 1.should != 2
  271.                         1.should_not equal(2) # same as above
  272.                         1.should_not == 2
  273.                         5.should be > 3
  274.                         5.should be <= 5
  275.                         (1.251).should be_close(1.25, 0.005)
  276.                         (1.251).should be_within(0.005).of 1.25 # >= RSpec 2.1
  277.                         "reg exp".should =~ /exp/
  278.                         [1,2].should include(1)
  279.                         1.should respond_to(:to_s)
  280.                        
  281.                         true.should be_true
  282.                         0.should be_true
  283.                         "this".should be_true
  284.                        
  285.                         lambda { Object.new.explodde! }.should raise_error(NameError)
  286.                        
  287.                         # nothing fits
  288.                         5.should satisfy { |it| it == 5 }
  289.                 end
  290.                
  291.                 it "shows cool things" do
  292.                         count = 1
  293.                         expect {
  294.                                 count = 3
  295.                         }.to change { count }.by(2)
  296.  
  297.                         expect {
  298.                                 # not changing
  299.                         }.to_not change { count }
  300.                        
  301.                         count = 1
  302.                         expect {
  303.                                 count = 3
  304.                         }.to change { count }.to(3)
  305.                        
  306.                         count = 1
  307.                         expect {
  308.                                 count = 3
  309.                         }.to change { count }from(1).to(3)
  310.                        
  311.                         # raise-rescue - exception handling
  312.                         expect {2 / 0}.to raise_error("divided by 0")
  313.                         expect {2 / 0}.to raise_error(/by 0/)
  314.                         expect {2 / 0}.to raise_error(ZeroDivisionError)
  315.                        
  316.                         # try-catch - expected circumstance handling
  317.                         lambda {  throw :room_is_full }.should throw_symbol(:room_is_full)
  318.                        
  319.                         # predicates
  320.                         nil.should be_nil #call nil.nil?
  321.                         [].should be_empty # calls [].empty?
  322.                         [1,2,3].should_not be_empty # calls [1,2,3].empty
  323.                        
  324.                         # convert anything that begins with have_ to a predicate on the target object beginning with has_
  325.                         {:id => 1}.has_key?(:id).should == true
  326.                         # can be written as
  327.                         {:id => 1}.should have_key(:id) # calls {:id => 1}.has_key?(:id)
  328.                        
  329.                         # collections                  
  330.                         obj = {}
  331.                         def obj.numbers
  332.                          [1,2,3,4]
  333.                         end
  334.                        
  335.                         obj.should have(4).numbers # calls obj.numbers.length
  336.                         [1,2,3,4].should have(4).items # 'items' is 'reserved' to say "ensure number of items on the collection"
  337.                         [1,2,3,4].should be_any {|n| n % 2 == 0} # [1,2,3,4].any? {|n| n %% 2 == 0}.should be_true
  338.                         "stringy".should have(7).charaters # same as items, just syntactic sugar
  339.                         [1,2,3,4].should have_exactly(24).items # same as 'have'
  340.                         obj.should have_at_least(3).numbers
  341.                        
  342.                        
  343.                 end
  344.         end # built-in matchers
  345.        
  346.        
  347.        
  348.        
  349.        
  350.         context "custom matchers" do
  351.                 # TODO: describe multiple ways
  352.                
  353.                 #define class
  354.                 class SimilarTo
  355.                         # mandatory - link to the object under test
  356.                         def initialize(it)
  357.                                 # object under test
  358.                                 @it = it
  359.                         end                    
  360.                         # mandatory - check the positive condition
  361.                         def matches?(that)
  362.                                 @that = that # save to use it in messages
  363.                                 @that.to_s.downcase.should == @it.to_s.downcase
  364.                         end
  365.                         # optional - opoosite to mathch?
  366.                         def does_not_matche?(that)
  367.                                 result = !matches?(that)
  368.                                 @that, @it = @it, @that # swap for negative condition or additionally cusomtize messages
  369.                                 result # don't forget to return
  370.                         end
  371.                         # mandatory
  372.                         def failure_message_for_should
  373.                                 "expected #{@it} to be similar to #{@that}"
  374.                         end
  375.                         # optional
  376.                         def failure_message_for_should_not
  377.                                 "expected #{@it} to be different from #{@that}"
  378.                         end
  379.                         #optional
  380.                         def description
  381.                                 "#{@it} should be similar to #{@that}"
  382.                         end
  383.                 end
  384.                
  385.                 #define method on example (see set-up to incude in all examples)
  386.                 def similar_to(that)
  387.                         SimilarTo.new(that)
  388.                 end
  389.         end # custom matchers
  390.        
  391.        
  392.        
  393.        
  394.         context "macros" do
  395.                 module ControllerMacros        
  396.                   def should_render(template)
  397.                         it "should render the #{template} template" do
  398.                           do_request
  399.                           response.should render_template(template)
  400.                         end
  401.                   end
  402.  
  403.                   def should_assign(hash)
  404.                         variable_name = hash.keys.first
  405.                         model, method = hash[variable_name]
  406.                         model_access_method = [model, method].join('.')
  407.                         it "should assign @#{variable_name} => #{model_access_method}" do
  408.                           expected = "the value returned by #{model_access_method}"
  409.                           model.should_receive(method).and_return(expected)
  410.                           do_request
  411.                           assigns[variable_name].should == expected
  412.                         end
  413.                   end
  414.  
  415.                   def get(action)
  416.                         define_method :do_request do
  417.                           get action
  418.                         end
  419.                         yield
  420.                   end
  421.                 end
  422.  
  423.                 RSpec.configure do |config|
  424.                   config.use_transactional_fixtures = true
  425.                   config.use_instantiated_fixtures  = false
  426.                   config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
  427.                   config.extend(ControllerMacros, :type => :controller)
  428.                 end            
  429.         end # macros
  430.        
  431. end # module