-
Notifications
You must be signed in to change notification settings - Fork 194
Testing Shoes
Shoes 4 is developed in a TDD style. You should be writing and running the specs. But with a project of this size, it can be hard to get your footing. This page should help you understand the testing layout in Shoes, and give you some tips and tricks along the way.
Shoes 4 is split into a [few separate gems](Gem structure), specifically to enable running with a different UI backend at some point in the future. This gives us a couple different main locations where specs can be found:
-
shoes-core/spec
- specs against the main Shoes DSL, relying only on a 'mock' backend (more on that in a moment) -
shoes-swt/spec
- specs for the current default backend implementation based on SWT + JRuby -
shoes-package/spec
- specs for Shoes' packaging functionality
When you know the purpose of each of the separate gems, it's a bit clearer what should be tested in each location.
-
shoes-core/spec
tests out DSL logic and algorithms and potentially interaction the DSL has with the backend. -
shoes-swt/spec
tests out how the backend directly integrates with the underlying SWT library. Much, although not all, of this will be done via expectations and mocking. -
shoes-package/spec
tests out anything related to packaging. It actually creates files on disk to accomplish this testing end-to-end (i.e. integration testing)
Many of the tests in shoes-core
look similar, since they often serve very similar roles--take values from the user, send them along to the backend. Among the simpler examples is shoes-core/spec/shoes/progress_spec.rb
A simple example from shoes-swt
is shoes-swt/spec/shoes/swt/color_spec.rb
. A more complex example of interactions with SWT can be found in shoes-swt/spec/shoes/swt/key_listener_spec.rb
So what's this mock backend that we talked about shoes-core
using? When the specs for shoes-core
are run, we load up a fake backend implementation from shoes-core/lib/shoes/mock/*
. Any method that the DSL expects to call against a backend should be implemented here (since some DSL code under test much call out to the mock backend, right?)
The mock backend serves two purposes then: it exists to let the DSL tests run without loading a full UI backend, and it is documentation of the methods that the DSL will call on a backend. When a new backend implementation gets started, the mock backend would be a great place to start since it essentially lists all the methods you need to provide!
The specs against the DSL in shoes-core
are also loaded when you run the specs under shoes-swt
. This provides us with additional coverage to make certain that the backend is not breaking any expectations the DSL has of it.
The easiest way to get started running specs are the available rake tasks. Some examples (run rake --tasks
to see a more complete list):
$ rake spec # Run the whole spec suite
$ rake spec:core # Run integration specs using the mock backend
$ rake spec:swt # Run integration specs using the Swt backend, plus isolation specs for the Swt backend
$ rake spec:package # Run specs for Shoes packaging
$ rake spec:swt:isolation # Run isolation specs for the Swt backend
$ rake spec:swt:integration # Run integration specs using the Swt backend
$ rake spec[Shape] # Run the whole spec suite, but only for Shape
$ rake spec:core[Shape] # Run integration specs for Shape using the mock backend
$ rake spec:swt[Shape] # Run integration and isolation specs for Shape, using the Swt backend
$ rake spec:swt:isolation[Shape] # Run isolation specs for Shape using the Swt backend
Note: For Windows, C:\tmp\shoes4>jruby -S rake spec
Sometimes you only want to run specs from individual files rather than entire suites. You can run individual specs from the project root directory like this:
$ rspec shoes-swt/spec/shoes/swt/app_spec.rb
If you're on OS X and you are running specs that require SWT, you will have to set the JRUBY_OPTS
environment variable first:
$ export JRUBY_OPTS=-J-XstartOnFirstThread
$ rspec shoes-swt/spec/shoes/swt/app_spec.rb
or set JRUBY_OPTS
directly on the command line:
$ JRUBY_OPTS=-J-XstartOnFirstThread rspec shoes-swt/spec/shoes/swt/app_spec.rb
As mentioned earlier, if we run a spec from shoes-core
it will use the backend. What if we wanted to run one of those files but with the SWT backend loaded?
While there's not a perfect fix, you can get around this easily by running an additional spec file from shoes-swt
, which will force the SWT backend to load.
$ rspec shoes-swt/spec/shoes/swt/app_spec.rb shoes-core/spec/shoes/app_spec.rb
Now when shoes-core/spec/shoes/app_spec.rb
is run, it'll be with SWT loaded.
There are two kinds of Shoes 4 specs:
-
Integration specs: These specify the functionality of the Shoes DSL. They can be run with any compatible Shoes backend. Shoes 4 comes with a mock backend and an SWT backend that can run the integration specs.
-
Isolation specs: These specify the internal behavior of a Shoes backend, in isolation from the DSL. Shoes 4 comes with an isolation spec suite for the SWT backend.
RSpec's mocking and stubbing support comes in very useful throughout Shoes. Some general principles to keep in mind:
- If you're testing in
shoes-core
, useexpect
and mocking in general to test the border between the DSL and the backend. Code entirely within the DSL should ideally be testable without resorting to mocking. - Similarly, when testing in
shoes-swt
you will often need to stub out or mock various behaviors in theswt
gem we use to access the underlying UI library. - In
shoes-swt
specs, you may also find it necessary to stub out the DSL for your backend components. This is because the lifetimes are backward (i.e. DSL's create backend objects) which poses some issues for properly wiring up tests.
Lots more good advice on spec'ing can be found at http://betterspecs.org/