Hypercuke helps you use Cucumber to do BDD at multiple layers of your application, and gently nudges you into writing your scenarios in high-level terms that your users can understand.
Because TATFT.
Cucumber is a great way to write acceptance tests that are also, by definition, integration tests. Each scenario defines a "walking skeleton" (I've also heard "tracer bullet") -- one complete path through the system from top to bottom, describing one feature that an end user actually cares about.
The traditional Rails approach of using Cucumber to test an app from a browser can be a bit tedious:
- In the short term, sometimes there's a LOT of new functionality to define, and you spend days or weeks just getting one scenario to pass. This can suck the morale right out of you.
More importantly, Cucumber has some long-term pitfalls:
-
Having all your tests fire up a web browser and exercise the web application gets really really slow (especially when every! single! test! starts with "Given I am logged in as a normal user").
-
More subtly, the implicit assumption that "Cucumber ==> web browser" makes it too easy for knowledge of the web UI to creep into tests -- and, as the Cucumber community has already learned (see The Training Wheels Came Off), this can lead to slow, brittle tests.
-
Cucumber tutorials tend to show you step definitions that combine a regular expression with an associated block of code. Then they say "TADA!" and wander off, leaving users with the impression that that's where all their code is supposed to go. Because everything is in a flat namespace, this tends to turn into... well, PHP. I've seen projects with thousands of lines of horrible procedural code in deeply interdependent step definitions that resist refactoring. In one particularly memorable instance, I actually helped write a 50-line step definition with a parameter named "destroy_the_earth".
Hypercuke's core idea is a clever way of using Cucumber tags. By swapping out different adapters for your step definitions, you can write a scenario once, tag it appropriately, and then execute that scenario to test any or all of:
- a fast core layer of plain old Ruby objects,
- ActiveRecord models,
- some Gourmet Service Objects,
- an API if you have one,
- the UI,
- or any other layer that's meaningful to you.
Hypercuke directly addresses each of the pain points described above:
-
By starting off at a low layer, you can use your Cucumber scenario as a short-span integration test that's just wrapped around a few simple objects. Once you're satisfied with how that works, you can move up to a higher level of abstraction. If a scenario is a "walking skeleton", testing at multiple layers lets you start by building the skeleton just up to the knees, then up to the base of the spine, and so on.
-
Just because your tests are in Cucumber doesn't mean they have to be slow. I originally developed Hypercuke because I wanted to describe all of my features using Gherkin, but only test one or two "golden path" scenarios through a web browser. Scenarios to describe boundary cases, exceptions, or variations can run against a lower layer of the application, which can have as much or as little overhead as makes sense for each scenario.
-
Because my scenarios might run at varying levels of abstraction, I write them in interface-agnostic language. (For example, I'll write "I view the list of widgets" instead of "I go to /widgets".) And if I forget, the cognitive dissonance when I write the step definitions very quickly reminds me to use more generic language. This helps me write tests at a high level of abstraction, and it also helps keep me focused on why I'm writing this feature, so I don't get lost TDDing my way through a gold-plated automated yak-shaving factory.
-
Finally, Hypercuke provides just enough structure for you to write reusable step definitions. Inside the regular-expression-plus-block that Cucumber gives you, you write the bare minimum amount of code you need to translate from Gherkin into a Ruby message, and then you send that message to a step adapter that does the work. Step adapters are Ruby objects, which means you can use all of your Ruby fu to keep your code organized.
TODO: continue here :D
Hypercuke adds a single #step_driver
method to the Cucumber "world". THIS
SHOULD BE ALL YOU NEED IN THE CUCUMBER WORLD. Ideally, a step definition
should consist of a single line of code that sends a message to step_driver
.
Within the step driver, you have access to various topics. A topic can be any interesting part of your application: you might have a topic that sets up various user accounts, one manages authentication, and one for each of the major entities in your application. Think of this as a vertical slice through your application. If you're familiar with UML sequence diagrams, a topic might give you access to one or more "objects" (those are the boxes along the top that have vertical "lifelines" below them).
If a Topic is a vertical slice through your application's features, a layer is a horizontal slice. A Layer defines how you interact with each Topic at a specific layer of abstraction (hence the name).
Step Adapters are an internal implementation detail of Hypercuke, and you mostly shouldn't have to worry about them. They are mentioned here purely for completeness. Every method you define using Hypercuke winds up living on a StepAdapter object.
This is probably easier to understand with a table, so...
- Top-level object available as #step_driver: Step Driver
- Area of domain: Topic
- Layer of application: Layer
- Intersection of the two: StepAdapter
- Name of generated StepAdapter class:
Hypercuke::StepAdapters::<Topic>::<Layer>
Here's a quick ASCII diagram swiped from one of the spec files:
T O P I C S
L +------------+------+-------+
A | Cheese | Wine | Bread |
Y +-------+------------+------+-------+
E | Core | SA* | SA* | SA* |
R | Model | SA* | SA* | SA* |
S | UI | SA* | SA* | SA* |
+-------+------------+------+-------+
* SA = StepAdapter
TODO: write me
I started out with the concept of "layers", so this gem was originally going to be called "cucumber-parfait". But as I worked through it, I kept visualizing things using two-dimensional matrices, which kept moving around in my brain as I thought about them... and that reminded me of visualizations of a hypercube. Ergo, Hypercuke.
Add this line to your application's Gemfile:
gem 'hypercuke'
And then execute:
$ bundle
Or install it yourself as:
$ gem install hypercuke
Obviously I have some more writing to do, but you'll need to add this line somewhere in your application's Cucumber environment (this is usually somewhere in /features/support/):
require 'hypercuke/cucumber_integration'
TODO: Write more detailed usage instructions
- Fork it ( https://github.com/geeksam/hypercuke/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request