Using RSpec with MacRuby, Part 2

Update 11/Dec/2009: The described solution doesn't work with MacRuby 0.5 as noted by Matt Aimonetti in the comments of the first part.

This is the second part of our small series about MacRuby and RSpec. In the first part, we have added RSpec to the example project MacRubyTwitterMG, did a small patch to make it run and defined the first two specs. Now we want to define some more specs and learn how to use mock objects:

  1. The next spec shall test the method refresh and looks like this:
    describe "#refresh" do
      it 'should refresh the tweets when username and password are given' do
        @main_controller.username = "ashtom" 
        @main_controller.password = "secret" 
        @twitterEngine.should_receive(:setUsername).with("ashtom", password:"secret")
        @twitterEngine.should_receive(:getFollowedTimelineSinceID)
        @main_controller.refresh(nil)
      end
    end
    
    For several reasons we do not really want to connect to Twitter here, so we try to mock the object @twitterEngine.
    before :each do
      @twitterEngine = mock("MGTwitterEngine")
      @twitterEngine.stub!(:initWithDelegate).and_return(@twitterEngine)
      MGTwitterEngine.stub!(:alloc).and_return(@twitterEngine)
    end
    
    The problem with this code is that MacRuby does not know the class MGTwitterEngine while running the specs from the command-line, because we have added all of its Objective-C source files directly to our Xcode project. A possible solution might be to create a framework for MGTwitterEngine, so let's try this.
  2. Select File > New Project..., then choose Cocoa Framework and continue with Choose.... Enter MGTwitterEngineFramework as the name of the new framework and put it into the Vendor directory which we've already created.
  3. Drag the group Vendor > MGTwitterEngine from the project MacRubyTwitterMG to the newly created project.
  4. Right-click the target MGTwitterEngineFramework, choose Get Info and change the following settings:
    • Architectures: 32/64-bit Universal
    • Objective-C Garbage Collection: Supported [-fobjc-gc]
  5. Close the inspector.
  6. Select x86_64 as the active architecture, then hit Build & Go.
  7. Now we change the file spec_helper.rb in the MacRubyTwitterMG project to include the new framework:
    framework File.expand_path(File.dirname(__FILE__) + '/../Vendor/MGTwitterEngineFramework/build/Debug/MGTwitterEngineFramework.framework')
    
  8. Back to the spec, we need to call the method awakeFromNib to initialize the instance of MGTwitterEngine:
    before :each do
      # ...
    
      @main_controller.awakeFromNib
    end
    
    As awakeFromNib is accessing the member @timelineTableView (which is an IBOutlet connected to the table view) we need to mock it as well:
    before :each do
      # ...
    
      @timelineTableView = mock("NSTableView")
      @timelineTableView.stub!(:dataSource=)
      @main_controller.timelineTableView = @timelineTableView
    
      @main_controller.awakeFromNib
    end
    
  9. We run the specs:
    $ macruby Spec/Controllers/MainControllerSpec.rb 
    ...
    
    Finished in 0.078637 seconds
    
    3 examples, 0 failures
    
  10. Done.

The complete code is (as always) available at GitHub.

Aftermath

After having defined some more specs I now get the following warning:

~/Projects/MacRubyTwitterMG/Vendor/RSpec/lib/spec/mocks/proxy.rb:201: 
warning: removing pure Objective-C method `proxied_by_rspec__alloc' may cause 
serious problem
This seems to be related to the stub of MGTwitterEngine.alloc, but so far I've found no way to prevent the warning. If you have a solution, please post a comment.

Keywords: mac, programming, ruby

Added by Thomas Dohmke 279 days ago


Comments

Add a comment

Twitter

Follow us on Twitter:

Keywords