Hi David - it's starting to sink in for me. Your explanation was clear but I have to ask if there is a better way when I hear that I'm using something opposite its intended purpose :)
I've been able to send a block to my shared example group successfully - working great so far! Thanks again On Fri, Oct 8, 2010 at 9:21 AM, David Chelimsky <[email protected]>wrote: > > On Oct 8, 2010, at 8:20 AM, Tim Gremore wrote: > > On Thu, Oct 7, 2010 at 8:25 PM, David Chelimsky <[email protected]>wrote: > >> On Oct 7, 2010, at 4:36 PM, Tim Gremore wrote: >> >> I'm stuck! Not sure what I'm missing but I'm struggling to get a shared >> example group working with my controller specs. Here is a piece of the >> backtrace: >> >> /Users/20217633/.rvm/gems/ruby-1.9.2...@rails3/gems/rspec-core-2.0.0.rc/lib/rspec/core/example_group.rb:68:in >> `it_should_behave_like': Could not find shared example group named "an admin >> is logged in" (RuntimeError) >> from >> /Users/20217633/apps/curriculum/spec/controllers/users_controller_spec.rb:5:in >> `block in <top (required)>' >> from >> /Users/20217633/.rvm/gems/ruby-1.9.2...@rails3/gems/rspec-core-2.0.0.rc/lib/rspec/core/example_group.rb:132:in >> `module_eval' >> from >> /Users/20217633/.rvm/gems/ruby-1.9.2...@rails3/gems/rspec-core-2.0.0.rc/lib/rspec/core/example_group.rb:132:in >> `subclass' >> from >> /Users/20217633/.rvm/gems/ruby-1.9.2...@rails3/gems/rspec-core-2.0.0.rc/lib/rspec/core/example_group.rb:119:in >> `describe' >> from >> /Users/20217633/.rvm/gems/ruby-1.9.2...@rails3/gems/rspec-core-2.0.0.rc/lib/rspec/core/extensions/object.rb:7:in >> `describe' >> from >> /Users/20217633/apps/curriculum/spec/controllers/users_controller_spec.rb:3:in >> `<top (required)>' >> >> I'm using the following: >> >> rails 3.0 >> rspec-rails 2.0.0.rc (and friends) >> cucumber-rails 0.3.2 >> factory_girl_rails 1.0 >> >> >> And the following for shared examples: >> >> module ExampleGroupMethods >> >> describe "an admin is logged in", :shared => true do >> before(:each) do >> controller.stubs(:logged_in? => true) >> controller.stubs(:current_user => Factory.create(:admin)) >> end >> end >> >> describe "a supporter is logged in", :shared => true do >> before(:each) do >> controller.stubs(:logged_in? => true) >> controller.stubs(:current_user => Factory.create(:supporter)) >> end >> end >> >> describe "a manager is logged in", :shared => true do >> before(:each) do >> controller.stubs(:logged_in? => true) >> controller.stubs(:current_user => Factory.create(:manager)) >> end >> end >> >> end >> >> >> And the follow controller spec: >> >> describe UsersController do >> >> it_should_behave_like "an admin is logged in" >> >> describe "handling /users" do >> >> before do >> @user = mock_model(User) >> end >> >> def do_get >> get :index >> end >> >> it "should assign users to the view" do >> User.should_receive(:all).with(:order => "last_name, >> first_name").and_return([ @user ]) >> do_get >> end >> >> end >> >> end >> >> >> And finally this spec_helper: >> >> # This file is copied to spec/ when you run 'rails generate rspec:install' >> ENV["RAILS_ENV"] ||= 'test' >> require File.expand_path("../../config/environment", __FILE__) >> require 'rspec/rails' >> >> # Requires supporting ruby files with custom matchers and macros, etc, >> # in spec/support/ and its subdirectories. >> Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} >> >> RSpec.configure do |config| >> # == Mock Framework >> # >> # If you prefer to use mocha, flexmock or RR, uncomment the appropriate >> line: >> # >> # config.mock_with :mocha >> # config.mock_with :flexmock >> # config.mock_with :rr >> config.mock_with :rspec >> >> # Remove this line if you're not using ActiveRecord or ActiveRecord >> fixtures >> # config.fixture_path = "#{::Rails.root}/spec/fixtures" >> >> # If you're not using ActiveRecord, or you'd prefer not to run each of >> your >> # examples within a transaction, remove the following line or assign >> false >> # instead of true. >> config.use_transactional_fixtures = true >> >> # see http://asciicasts.com/episodes/157-rspec-matchers-macros >> config.include(ControllerMacros, :type => :controller) >> >> # see http://pastie.org/870928 >> config.include(ExampleGroupMethods, :type => :controller) >> end >> >> >> Anyone have a suggestion? Thanks in advance >> >> >> Shared examples have changed significantly in rspec-2. See >> http://github.com/rspec/rspec-core/blob/master/features/example_groups/shared_example_group.feature >> to >> get a feel for how they work now. >> >> The first thing is that :shared = true no longer works. You need to use >> shared_examples_for. The other big change is that it_should_behave_like >> creates a nested group, so if you do this: >> >> shared_examples_for "foo" do >> it "does something" do >> end >> end >> >> describe "something" do >> it_behaves_like "foo" # it_behaves_like is an alias for >> it_should_behave_like >> end >> >> That's the same as doing this: >> >> describe "something" do >> context "behaves like foo" do >> it "does something" do >> end >> end >> end >> >> This is a good thing, because it prevents the context of the shared group >> from polluting the including group's scope, which can lead (and has led) to >> a lot of confusion. >> >> Now in your case, you're using the shared group as a means of sharing >> before blocks. While that works, it's the opposite of the intent of shared >> groups, which is that the enclosing group should provide the context for the >> shared group. What you _can_ do, however, to maintain the structure you >> have, is to pass a block to it_should_behave_like, so your example change >> from this: >> >> # rspec-1 >> describe UsersController do >> it_should_behave_like "an admin is logged in" >> describe "handling /users" do >> before do >> @user = mock_model(User) >> end >> >> def do_get >> get :index >> end >> >> it "should assign users to the view" do >> User.should_receive(:all).with(:order => "last_name, >> first_name").and_return([ @user ]) >> do_get >> end >> end >> end >> >> to this: >> >> #rspec-2 >> describe UsersController do >> it_should_behave_like "an admin is logged in" do >> describe "handling /users" do >> before do >> @user = mock_model(User) >> end >> >> def do_get >> get :index >> end >> >> it "should assign users to the view" do >> User.should_receive(:all).with(:order => "last_name, >> first_name").and_return([ @user ]) >> do_get >> end >> end >> end >> end >> >> HTH, >> David >> > > Thanks much Justin and David - very helpful. David, you explained that I >> could accomplish my goal by sending a block to it_should_behave_like, even >> though that is not its intended purpose. Is there a cleaner way to >> accomplish my goal (spec'ing controller authentication and authorization)? > > > Not sure if I was clear. I didn't mean to imply that passing a block a bad > thing. Quite the contrary. Passing blocks is a big part of what makes shared > groups in rspec-2 better than rspec-1 for their intended purpose: > > to share examples for a conceptual (duck) type or a module, with the > including group providing concrete context. > > What you're doing is the opposite - providing context using a shared > group. > > Does that make sense? This is not to say that this is wrong, or evil. It's > just the reverse of the intent. > > Take a close look at this scenario: > http://github.com/rspec/rspec-core/blob/c5e6bc031404762edcda7789ecc04c6d340c8ef6/features/example_groups/shared_example_group.feature#L11. > It demonstrates shared examples for a collection, applied to two different > types, with the context provided by the "describe Array" group (which > provides an instance of Array as the implicit subject) and the "describe > Set" group (which provides a Set). > > Let me know if you have any questions. > > Cheers, > David > > > _______________________________________________ > rspec-users mailing list > [email protected] > http://rubyforge.org/mailman/listinfo/rspec-users > -- Tim Gremore 920 471-1716
_______________________________________________ rspec-users mailing list [email protected] http://rubyforge.org/mailman/listinfo/rspec-users
