HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Mixing Watir::Browser into RSpec

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
watirintobrowsermixingrspec

Problem

In my previous Watir question I was making a module with session_FF accepting a block.

Now I want to make the same but via RSpec. Here is my try:

require "rspec"
require "watir"

module Session_FF
  def method_missing m, *args, &block
    @browser.send m, *args, &block
  end
  def element *args
    elements = @browser.elements *args
    case elements.size
    when 1 ; elements.first
    when 0 ; raise "found no nodes using search args: #{args}"
    else   ; raise "found #{es.size} nodes -- use #elements method or improve args"
    end
  end
end

RSpec.configure do |config|
  config.include Session_FF
  config.before{ @browser ||= Watir::Browser.new :ff}
  config.after{ @browser.quit }
end

describe "it" do
  it "is alive" do
    goto "http://example.com/"
    element(class: "userpanel").click
  end
end


I see a problem, that previously I was defining method_missing in my own module but now include may redefine RSpec::Core::ExampleGroup or smth's #method_missing if it already exists or may appear in RSpec implementation. What is a better way for method_missing approach to forward browser interaction methods into @browser?

Solution

As far as I can tell, you have 4 options:

-
Use the current code as-is (and, as you say, risk stepping on an existing method_missing).

-
You can use RSpec's config.extend to at least limit the inclusion.

-
You can skip method_missing in favor of creating a module explicit method declarations for anything you want to forward to @browser. This is no doubt the safest, but also the most laborious, solution. Still, I'd consider it

-
You can do something like below, which, admittedly, is somewhat hacky.

module Session_FF

  # Alias existing method_missing implementation when being included
  def Session_FF.append_features(target)
    if target.instance_methods.include?(:method_missing)
      target.send(:alias_method, :overriden_method_missing, :method_missing)
    end
    super
  end

  # Define your own method_missing which forwards to @browser
  # or to the previous method_missing (if any)
  def method_missing(name, *args, &block)
    if @browser.respond_to?(name)
      @browser.send(name, *args, &block)
    elsif respond_to?(:overriden_method_missing)
      overriden_method_missing(name, *args, &block)
    end
  end

  def element(*args)
    # ...
  end

end


Should work, although if @browser also relies on method_missing without a reliable respond_to?, you might have better luck simply trying to call a method @browser and rescue a NoMethodError. Not very pretty, though.

On a side-note, I think it's perhaps a little strange that the module is named _FF when the @browser could be something other than Firefox. WatirSession might be a more inclusive name.

Code Snippets

module Session_FF

  # Alias existing method_missing implementation when being included
  def Session_FF.append_features(target)
    if target.instance_methods.include?(:method_missing)
      target.send(:alias_method, :overriden_method_missing, :method_missing)
    end
    super
  end

  # Define your own method_missing which forwards to @browser
  # or to the previous method_missing (if any)
  def method_missing(name, *args, &block)
    if @browser.respond_to?(name)
      @browser.send(name, *args, &block)
    elsif respond_to?(:overriden_method_missing)
      overriden_method_missing(name, *args, &block)
    end
  end

  def element(*args)
    # ...
  end

end

Context

StackExchange Code Review Q#67839, answer score: 5

Revisions (0)

No revisions yet.