Ask for Help
- “Can you use Google Maps on an https page?”
- Probably via an iframe. Is there a preferred way?
One of our client Java projects is pioneering the use of RSpec Story Framework for functional tests.
RSpec provides two frameworks for writing and executing examples of how your application should behave:
JRuby is an 100% pure-Java implementation of the Ruby programming language.
How can Java projects benefit from RSpec Story Framework? Let’s consider an example.
There is a Java back-end application server with proprietary remote interface. The server manages current states of all patents reviewed by multiple patent offices. Close to real life examples below have no proprietary information and object names has been changed. RSpec can help describe how server works and should be used, also what happens in special conditions.
one of stories from the server specification:
Story: patent review by counsel
A Council review of a patent should change the patent review state
and affect rolled up state of the patent application case.
Scenario: legal counsel reviews patent
Given a patent
And the patent roll up state is ACTIVE
When the legal council reviews the patent
Then patent state should be ON_HOLD
And patent case rolled up state should be SUSPENDED
Scenario: legal counsel releases patent from the review bin
Given a patent
And the patent roll up state is ON_HOLD
When the legal council release the patent from the review bin
Then the patent state should be ACTIVE
And the patent case rolled up state should be ACTIVE
Steps define our Domain Specific Language or DSL
steps_for(:patent_state_operations) do
Given "a patent" do
$patent_state_action_stub = PatentStateActionManager.new("local")
$patent_id = Random.new
end
Given "the patent roll up state is '$state'" do |state|
patent_state_action_stub.setState(patent_id, state);
end
When "a '$reviewer' releases the patent from the review bin" do |type|
newState = patent_state_action_stub.releaseFromBin(patent_id, reviewer);
end
When "a '$reviewer' reviews the patent" do |type|
newState = patent_state_action_stub.addToBin(patent_id, reviewer);
end
Then "patent state should be '$state'" do |state|
newState.should be state
end
end
loads required Java classes using JRuby:
require 'rubygems'
require 'spec'
require 'spec/story'
require 'java'
include_class 'client.project.functionaltests.PatentStateActionManager'
class Spec::Story::Runner::ScenarioRunner
def initialize
@listeners = []
end
end
Dir[File.dirname(__FILE__) + "/../steps/*.rb"].uniq.each { |file| require file }
JRuby runs all the stories for the project:
dir = File.expand_path(File.dirname(__FILE__))
require "#{dir}/helpers/helper"
with_steps_for :patent_state_operations do
run "#{dir}/stories/councilReview.txt"
end
And since we are on Java Project developers use the above Ruby steps to delegate most of the calls to some Java manager, which serves as a fixture for the functional tests.
This is a Java fixture to pass data to the application via RPC
public class PatentStateActionManager {
public PatentStateActionManager(String instanceType) {
... // get connection to server via RPC
}
public String addToBin(long patentId, String review) {
... // call RPC method
}
public String releaseFromBin(long patentId, String review) {
... // another RPC call
}
public void setState(long patentId, String newState) {
... // java land, Java developers do what they want
}
}
As a result we have a executable text specification described in close to English language. Majority of the boiler-plate code contained in Java fixtures and Ruby steps.
Project Managers and Quality Engineers can use RSpec stories to define and exercise the behavior of the application.
Java Developers implemented a few stories to demonstrate the use of all steps for Quality Engineers. In addition to Java unit tests that cover the project code, functional tests cover the application use cases.
Quality Engineers will fill the story book with more stories covering many conditions and states.
Java Developers will provide support for fixtures as interfaces evolves over the time.
Customers will define future stories as Pending stories.
And Test Valentines are moving to wider adoption of RSpec including:
outside of work, I do some things with OpenBSD. Over the last month or so, I’ve been poking at the DRI code, mostly to make it compile on my laptop. Why is it taking so long? I don’t know C, and are learning on the fly. Kernel space makes it interesting.
One problem with compiling: the code uses ‘vtophys()’, which is not portable. My laptop is running in AMD64 mode, which doesn’t have vtophys(). The solution: ‘bus_space_vaddr()’. it essentially is a drop-in replacement for vtophys(), but I have to set up the bus_space area first. Thats proving to be a little more of a challenge than I hoped. I think I’ll have to edit the headers, and add (yuck) some entries into a struct, so I can pass around the appropriate information.
Howdy, everyone! Don’t you hate it when one of your favorite Pivots blogs a non-official, non-blabs article about tea and you didn’t even know about it? It’s just too much effort to subscribe to everyone’s blog individually.
Fret no more! Now you can read and subscribe to every blog post with one easy URL: the All Pivots Blog. Just navigate to http://pivots.pivotallabs.com /everyone. You’ll see all Official, Blabs, and individual posts in chronological order.
A couple of points:
Desert has been upgraded to work with Rails 2, solving problems with ActionMailer templates and Rails load paths.
A reminder: RSpec supports nested describe blocks. This can be useful for sharing setup and also for organization (one describe per method being tested, with multiple it blocks). Some people reported that there are some issues when using nested describe blocks with fixture scenarios. There was also a report of some flakiness around a single it block being run more than once.
One of our projects has reported that they have been using NetBeans and are happy with it.
Many people (including me) have complained about the lack of a good GUI debugger for Ruby. Now that some are finally getting usable, I’ve found I actually prefer IRB-style ruby-debug to a GUI.
There’s good tutorial links on the ruby-debug homepage, and a very good Cheat sheet, but I wanted to give a bare-bones HOWTO to help you get immediately productive with ruby-debug.
$ gem install ruby-debug
$ gem install cheat
$ cheat rdebug
$ vi ~/.rdebugrc
set autolist
set autoeval
set autoreload
$ rdebug script/server
(rdb:1) b app/controllers/my_controller.rb:10
require 'ruby-debug'
debugger
my_buggy_method('foo')
(rdb:1) cat RuntimeError
(rdb:1) c
(rdb:1) n
(rdb:1) s
(rdb:1) c
(rdb:1) where
(rdb:1) l=
(rdb:1) myvar.class
(rdb:1) @myvar = 'foo'
(rdb:1) h
There are many other commands, but these are the basics you need to poke around. Check the Cheat sheet for details.
This can also be used directly from any IDE that supports input into a running console (such as Intellij Idea).
That should get you started. So, before you stick in another ‘p’ to debug, try out ruby-debug instead!
By default, Ajax.Updater will send a POST request to whatever URL is specified. That’s fine, and it prevents certain browser caching issues, but it won’t play well with Rails’ resource routes. Rails will map POST requests to the create controller action, which is not always desirable.
For example, let’s say we have an Ajax.Updater that refreshes a list of posts (GET /posts). Unfortunately, the request that is created by default will be different (POST /posts). You can force the GET method, like so
remote_function(:update => "posts_container",
:url => posts_path, :method => :get))
One problem remains, however. If the URL for the remote_function and the URL for the full (html) version of the resource are the same, as they probably ought to be, and if the Ajax call is a GET request, Firefox will cache the data returned by the remote_function call as the most recent version of the page /posts. When a user navigates away from /posts, and subsequently decides to return to /posts by hitting the back button, the remote_function data will be displayed (as opposed to the full html version of the page). That’s not very user-friendly.
To avoid this, one will need to set the appropriate header:
headers["Cache-Control"] = "no-store"
Interestingly enough, IE7 appears to deal with this situation correctly.
We all love to Monkey Patch Rails and other Ruby apps. However, we sometimes want to target these patches to the specific versions where they are needed.
Here’s the easiest way to do this, via RubyGem’s built-in version requirement support. The version 0.11.0 should indeed be greater than version 0.9.0:
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> Gem::Version::Requirement.new(['> 0.9.0']).satisfied_by?(Gem::Version.new('0.11.0'))
=> true
Notice that you can’t do this with string comparison, because with a per-character comparison,1 is not greater than 9:
irb(main):001:0> '0.11.0' > '0.9.0'
=> false
Here’s a little class which puts some helper and example methods around this approach (these methods are all in real use for some of our multipart mailer hacks):
module Pivotal
class VersionChecker
def self.current_rails_version_matches?(version_requirement)
version_matches?(Rails::VERSION::STRING, version_requirement)
end
def self.version_matches?(version, version_requirement)
Gem::Version::Requirement.new([version_requirement]).satisfied_by?(Gem::Version.new(version))
end
def self.rails_version_is_below_2?
result = Pivotal::VersionChecker.current_rails_version_matches?('<1.99.0')
result
end
def self.rails_version_is_below_rc2?
Pivotal::VersionChecker.current_rails_version_matches?('<1.99.1')
end
def self.rails_version_is_1991?
Pivotal::VersionChecker.current_rails_version_matches?('=1.99.1')
end
end
end
(note: some angle brackets changed due to code formatting bug)
Here’s an example of how you’d use this:
if Pivotal::VersionChecker.rails_version_is_below_2?
# do some backward compatibility stuff
# or handle bugs that have been fixed in Rails > 2
end
Note that this is only possible now that Rails has started using a more sensible strategy for versioning edge gems and improved support for using advanced versioning with RAILS_GEM_VERSION.
For many projects, this may be overkill. It is useful at Pivotal, though, where many various projects may be on different rails versions, but still want to use the latest common core libraries (and monkey patches) without having to upgrade Rails for their app.
This isn’t only useful for monkey patching. It can be handy for any library that wants to be backward- or forward-compatible with its dependencies. I’ve used this approach at Pivotal and on my personal projects to have Continuous Integration automatically run my tests against multiple dependency versions, without having to change anything other than the CI project name:
There are numerous other related topics for discussion in this area, such as the power of versions or the wisdom of freezing, but I’ll save those for future posts. Even if you do freeze the trunk of Rails/plugins/gems, since the version is included in the source, this approach should work barring any conflicts with trunk changes since the last release.
Happy Versioning!