Skip to content

torquebox/torquespec

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Integration Testing for TorqueBox

TorqueSpec

TorqueSpec extends the RSpec testing framework to provide integration testing for applications designed to run on the TorqueBox Ruby application server. Before running your RSpec examples, TorqueSpec ensures that a TorqueBox server is running and enables you to verify expectations against real applications deployed on it.

Easy browser control/simulation is possible via the capybara or webrat gems, of course, but TorqueSpec also provides “in-container” testing, where your examples are actually run within the TorqueBox server itself.

Installation

Install the torquespec gem:

$ gem install torquespec

And then in your spec files (or indirectly via spec_helper.rb):

require 'torquespec'

Doing this will cause TorqueSpec to add a “before suite” hook that fires up a TorqueBox server, i.e. JBoss, prior to running your specs and an “after suite” hook to shut it down.

RSpec extensions

Aiming to be minimally intrusive, TorqueSpec makes two new methods available to your spec files: deploy and remote_describe.

deploy()

Determines which app[s] are deployed prior to running a particular ExampleGroup, so typically called from a top-level describe block. The deployment and subsequent undeployment occur within “before all” and “after all” RSpec hooks, respectively.

The method accepts any number of arguments, each of which should represent a TorqueBox deployment descriptor, typically a YAML “here document”, a Hash, or a path to an existing file. It also accepts a block, the result of which should also be one or more deployment descriptors. Here’s an example using a single here doc:

describe "basic test with heredoc" do

  deploy <<-DD_END.gsub(/^ {4}/,'')
    application:
      root: #{File.dirname(__FILE__)}/../app
      env: development
    web:
      context: /basic
  DD_END

  it "should work" do
    visit "/basic"
    page.should have_content('it worked')
    page.find("#success")[:class].should == 'basic'
  end

end

Here docs are especially nice because they support string substitution. Note that we’re setting the location of the app relative to the location of the spec file itself, e.g. __FILE__. And the little gsub(/^ {4},'') trick allows you to indent the YAML relative to the Ruby.

remote_describe()

TorqueSpec supports “in container” testing via the remote_describe method. It behaves exactly like the RSpec describe method, but the resulting ExampleGroup will be run inside the TorqueBox server, which is remote relative to the local RSpec process.

If your spec calls remote_describe, TorqueSpec will deploy a small daemon with your application. This daemon will act as an additional RSpec runner to which the “local” runner will delegate the invocation of those ExampleGroups defined by remote_describe. This delegation occurs via DRb: the “remote” runner reports its results to the “local” one, allowing you to summarize the results of all your tests, both in-container and out, in a single report.

Because your specs are consumed by two separate RSpec processes, you must ensure that any gems other than TorqueSpec/RSpec are available to the process that needs them.

require 'torquespec'
require 'torquebox-core'

TorqueSpec.local {
  require 'capybara'
}

app = <<-END.gsub(/^ {4}/,'')
    application:
      root: #{File.dirname(__FILE__)}/../app
    END

describe "local test" do
  deploy(app)

  it "should work" do
    visit "/"
    page.should have_content('it worked')
  end
end

remote_describe "remote test" do
  include TorqueBox::Injectors
  deploy(app)

  it "should work" do
    some_service = fetch(com.foo.SomeService)
    some_service.run.should == 'SUCCESS'
  end
end

The above spec shows two example groups, one local and one remote. There are a few things to note:

  • This spec will be processed by both the local test runner and the remote daemon.
  • The local example needs the visit() method and page objects from capybara so its require statemtent is guarded in a TorqueSpec.local block, which won’t be run remotely, since the capybara won’t be available in the daemon’s runtime. Similarly, put statements that should only run remotely in a TorqueSpec.remote block.
  • TorqueBox injection is supported in remote examples as long as TorqueBox::Injectors is included in their group.
  • Because the remote_describe block is evaluated locally (but not executed), we must require ‘torquebox-core’ to refer to TorqueBox::Injectors both locally and remotely, even though the injection will only be performed remotely.

Nesting Example Groups

The above example is not very efficient because the same application is being deployed twice: once for the local describe and again for the remote_describe. We can do better by taking advantage of RSpec’s support for nested groups:

describe "outside the container" do

  before(:each) do
    puts "runs both locally and remotely"
  end

  deploy <<-END.gsub(/^ {4}/,'')
    application:
      root: #{File.dirname(__FILE__)}/../app
  END

  it "should handle web requests" do
    visit "/"
  end

  remote_describe "inside the container" do

    before(:each) do
      puts "runs only remotely"
    end

    it "should handle injection" do
      fetch( 'service-registry' ).should_not be_nil
    end

  end

end

Example groups may be arbitrarily nested, but remember that ALL subgroups of a remote_describe will be executed remotely by the TorqueSpec daemon.

Also note that before and after blocks should work as you expect, but the fact that the parent’s before block in the above example will run remotely might mean you’ll need to guard certain statements in either a TorqueSpec.local or TorqueSpec.remote block.

Configuration

Configuration parameters are variables in the TorqueSpec namespace, e.g. TorqueSpec.max_heap.

ParameterDescriptionDefault
knob_rootWhere TorqueSpec creates your deployment descriptors.torquespec
jboss_homeWhere JBoss is installed$JBOSS_HOME
max_heapThe maximum RAM allocated to the JBoss JVM1024 MB
jvm_argsArguments to the JVM running JBosssee below
drb_portThe in-container spec runner listens on this port for requests7772
lazyWhether to use a running JBoss and leave it running when donefalse
default_deployWhere inside of knob_root to check for an optional default deployment descriptordefault_deploy.yml

By default, TorqueSpec is initialized thusly:

TorqueSpec.configure do |config|
  config.drb_port = 7772
  config.knob_root = ".torquespec"
  config.jvm_args = "-Xms64m -Xmx1024m -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -Dgem.path=default"
end

Include a similar block in your spec_helper.rb to customize any of these.

Dependencies

TorqueSpec has been extensively tested with RSpec 2, though RSpec 1 should be compatible as well. But really, why are you still using RSpec 1?