Ruby mock objects drop parameters


Maybe it’s me but it sure seems like Ruby mocks are dropping the parameters passed in from Objective-C. I’m about to give up on this venture into RSpec for iPhone controller code development. In my mind I see it as a huge possibility and I’m willing to give it a few more shots before I wave the white flag. Here’s what I observed so far. (Keep in mind this is coming from a guy who just learned Objective-C, XCode, Ruby, and RubyCocoa reading Apple docs in the shower this morning!) If I call into a Ruby mock passing an int primitive directly from Objective-C while running under RSpec on the 10th day of the 11th month with 3/4th of my coffee mug occupied with the ol’ black magic juice (sorry but from my mobile experience it’s definitely important to set the stage listing all of your environment variables and making sure the stars are all aligned…) then I get a program crash. If I instead pass an NSNumber object wrapping the int then the parameter is silently ignored. If I call into a simple Ruby object defined inline in the spec then the parameter seems to come through just fine. If I then wrap the Ruby Mock with my own crude inlined mock passing the value from ObjC through my mock and into the Ruby Mock then all is right with the world… abusive husbands across America stop beating their wives while they join hands and sing Koom-by-ya… aggressive drivers let you over in heavy traffic, and the DOW jumps up 200 points. To see what I mean, here’s the updated spec code from my earlier posting:

bowling_controller_spec.rb

require File.dirname(__FILE__) + ‘/test_helper’

require “BowlingController.bundle”
OSX::ns_import :BowlingController
OSX::ns_import :BowlingProtocol

include OSX

class MyMock
attr_accessor :delegate
def roll(num)
puts “Number is #{num}”
puts “Calling delegate #{@delegate}…”
@delegate.roll(num)
puts “Done with delegate…”
end
end

describe BowlingController do
before(:each) do
@controller = BowlingController.new
@bowling = mock(‘BowlingProtocol’)
@text_field = mock(‘Pins’)
@text_field.stub!(:intValue).and_return(10)
@controller.pins = @text_field
end

it “should roll a ball” do
@controller.roll
end

it “should roll a ball and get the value from the pins outlet” do
@text_field.should_receive(:intValue).and_return(0)
@controller.roll
end

it “should be an OSX::NSObject” do
@controller.is_a?(OSX::NSObject).should == true
end

it “should have an outlet to a bowling object” do
@controller.bowling = @bowling
end

it “should send the pin value to the bowling object” do
mock = MyMock.new
puts “Setting delegate to #{@bowling}”
mock.delegate = @bowling
puts “Now passing mock into real code…”
@controller.bowling = mock
# @controller.bowling = @bowling
@bowling.should_receive(:roll).with(NSNumber.numberWithInt(10))

@controller.roll
end
end

I updated the controller to use a protocol:
BowlingController.h

//
// BowlingController.h
// BowlingController
//
// Created by FIXME on 2008-11-10.
// Copyright 2008 FIXME. All rights reserved.
//

#import

@class UITextField;
@protocol BowlingProtocol;

@interface BowlingController : NSObject {
UITextField* pins;
id bowling;
}
@property (nonatomic, retain) UITextField* pins;
@property (nonatomic, retain) id bowling;

-(void) roll;
@end

bowling_controller_spec.rb

//
// BowlingController.h
// BowlingController
//
// Created by FIXME on 2008-11-10.
// Copyright 2008 FIXME. All rights reserved.
//

#import “BowlingController.h”
#import “BowlingProtocol.h”

@implementation BowlingController
@synthesize pins;
@synthesize bowling;

-(void) roll{
int val = [self.pins intValue];
[self.bowling roll:[NSNumber numberWithInt:10]];
}

@end

// This initialization function gets called when we import the Ruby module.
// It doesn’t need to do anything because the RubyCocoa bridge will do
// all the initialization work.
// The rbiphonetest test framework automatically generates bundles for
// each objective-c class containing the following line. These
// can be used by your tests.
void Init_BowlingController() { }

BowlingProtocol.h

@protocol BowlingProtocol
-(void) roll:(NSNumber*)pins;
@end

More on this later as I explore…

Leave a comment