Skip to content

Instantly share code, notes, and snippets.

@leewaa
Created May 21, 2014 08:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leewaa/30a7ce420c4076566d61 to your computer and use it in GitHub Desktop.
Save leewaa/30a7ce420c4076566d61 to your computer and use it in GitHub Desktop.
A small post about how I had been using rspec for rubymotion incorrectly and how it works properly.

Rubymotion notes

Testing

This line should be added to the app delegate didLaunchWithOptions method

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    return true if RUBYMOTION_ENV == 'test'
    # ...

This pretty much returns if the app is in testing mode. This is important because when running tests a window should not be created. Specs automatically create a window and controller (shown below) so having the app delegate also create a window will cause some bugs. You should still add in any initialization code thats required for setup before that line.

When testing a view controller it is important to specify what class is being tested. Lets say I have a controller called FooController, the corresponding spec file would look like so:

describe “The 'FooController' do
	tests FooController
	
	#Your specs go here.
end

The tests FooController line tells Bacon what class is actually being tested. This will instantiate a new window and new instance of the specified class. These are then available in the specs as controller and window. So that means I could do something like this:

describe ProjectsScreen do
  tests ProjectsScreen

  it "is the right class" do
    controller.should.be.kind_of ProjectsScreen
  end

  it "does contain a table view" do
    controller.tableView.should.not == nil
  end

  it "does display projects" do
    controller.tableView.visibleCells.should.not.be.empty
  end

  describe "Selecting a project" do
    before do
      @project = controller.projects[0]
      tap "#{@project.name}"
    end

    it "opens the projec_screen when a selection is made" do
      controller.project_screen.parent_screen.should == controller
    end

    it "passes though the selected project" do
      controller.project_screen.current_project.should == @project
    end
  end
end

One can override the controller by doing something like

describe ProjectsScreen do
  tests ProjectsScreen

  def controller
    @controller ||= ProjectsScreen.new(nav_bar: true)
  end

  #specs
end

However, the problem here is if any of your specs push stuff onto the screen then you will get a warning saying that the view is not in the window hierarchy. This makes total sense… In the obove example the controller now returns a new ProjectScreen which is totally on its own, isolated from the app.. It hasn’t been added onto any view that is some how linked to the window.

Before I knew how the previously mentioned stuff worked I had tried crerating a new window and setting it as the key window where the object I was testing is the rootViewController.

describe ProjectsScreen do
  tests ProjectsScreen

  def controller
    projectsScreen = ProjectsScreen.new(nav_bar: true)
    
    window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    window.rootViewController = projectsScreen
    window.makeKeyAndVisible

    @controller ||= projectsScreen
  end

  #specs
end

However this doesn’t change a thing and messes up other things. So to stay safe. Just do it the way I mentioned above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment