PSpec = RSpec + Cucumber for Perl 6

I've been playing with Ruby lately, and am quite enjoying it.
It seems like a very clean language, and has a lot of good features going
for it as well, including testing frameworks such as
RSpec and
Cucumber. Being me, I wondered what a
Perl 6 version would look like. Also being me, I decided that the best way
to find out, would be to write it.

Now yes, I know that
some people don't
have a very good opinion of RSpec, and it's unusual syntax,
but I thought it was an interesting way of setting up tests.
Agree or disagree, many people seem to be using (and liking) RSpec, and I
thought it only fair to make something like it available to the Perl 6
community.

Now, before you
download this,
look at it and scream at me with "hey, this is a tiny, minor subset of RSpec,
it's not even close to being a complete port!" I know.
I wasn't intending to write a complete Perl 6 version of RSpec. All I wanted
to do was write something that would allow you to use a similar syntax to
the one found in the example on the front page of the RSpec website.
It does some things in really crazy ways, but it's relatively fast and is
a fun example of what you can do with Perl 6. It's not meant to be taken
too seriously, but if you like it, go ahead and use it for something cool.
It's released under the
Artistic
License 2.0,
as used by Perl 6 itself.
Since the original version of this article was written, PSpec has gained a
lot more functionality, some of which is outlined below.

The Specification Format

So, the original example from the RSpec website looked something like this:

So yeah, it looks similar, but there are some significant differences between
the two. First of all, instead of the describe statement consisting
of a bunch of it statements, it now contains an Array of Pair objects.
The key of the Pair object is the name of the test, and the value of the Pair
is the closure containing the test itself.

Next up, there was no native .times method in the Int or Num classes,
and since method calls on objects require () brackets, I figured the prettiest
way to get the equivilant of 20.times { code } was to define an
infix operator called times that took an integer on the left hand
side, and a code block on the right hand side. Easy cheesy!
20 times { code } was born.

Finally, the syntax of the should call.
As mentioned before, without using some grammar modifying rules
(which I could have done, but didn't want to) trying to implement a
.should method would have meant using () brackets.
Not as pretty. So instead, I used multiple dispatch and created the
should-be infix operator instead. Given numeric values, it uses the
== comparision, given string values it uses
eq comparision. Given anything else, it uses the magical
Perl 6 entity known as the ~~smart match operator.
If the match is a success, it returns normally. If not, a diagnostic message
is output in TAP compliant format to help track down the bug:

# expected: 50
# got: 53

So, I know this is just a silly bit of code to partially emulate a pre-existing
package, but hey, it was fun.

A later update I added support for a special pair
syntax that allows you to specify a string for the 'expected' value and
a closure containing the test you want to run, in a format like:

PickleSandwich: Cucumber stories in PSpec

So, after working on the original release of PSpec,
I decided that it would be great if it could
also offer a story handling mode like
Cucumber. So, I added it.
You tell it a story and write rules for how to handle the
story. Just for something to give you an idea, here is a story for a simple
reverse polish notation calculator:

Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario: Add two numbers
Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen
Scenario: Add five numbers, with implicit add
Given I have entered 10 into the calculator
And I have entered 20 into the calculator
And I have entered 30 into the calculator
And I have entered 40 into the calculator
And I have entered 50 into the calculator
Then the result should be 150 on the screen
Feature: Subtraction
In order to do our bookkeeping
As a total moron
I want to be told the difference between two numbers
Scenario: Subtract two numbers
Given I have entered 50 into the calculator
And I have entered 20 into the calculator
When I press subtract
Then the result should be 30 on the screen
Feature: Multiplication
In order to show this is up with the times
As a calculator of four functions
I want to see it multiply
Scenario: Multiply two numbers
Given I have entered 20 into the calculator
And I have entered 5 into the calculator
When I press multiply
Then the result should be 100 on the screen
Feature: Division
In order to show how modular this is, it must be divided
As a purveyor of bad puns
I want to see the quotient
Scenario: Divide two numbers
Given I have entered 100 into the calculator
And I have entered 4 into the calculator
When I press divide
Then the result should be 25 on the screen
Scenario: Intentionally broken test
Given I have entered 100 into the calculator
And I have entered 25 into the calculator
When I press divide
Then the result should be 5 on the screen

The handler code is fairly simple too, you can test it yourself by running
the ./spec/calc.t specification test. It shows how to dispatch
based on the story, including how to perform tasks, such as clearing the
calculator's memory between each scenario. The handle-story method uses
TAP compliant output, while at the same time, reading off the story.
There is currently no support for Backgrounds or Scenario Outlines,
but the above story works just fine.
Advanced features such as Backgrounds and Scenario Outlines are available,
see the tests in the 't' folder of the source tree for more details.
Hey, as an extra bonus, it comes with a simple RPN calculator object class.
How cool is that ;-)

A big thanks to Carl Masak who suggested a method of implementing a
.times method in the Int class. Now you can use either
the times infix operator as shown above or you could use:

20.times: { say"hello"; }

Download

Since posting the original version of this article, it has been updated a
few times. I have also moved the code to Github instead of keeping it in
a tarball. Feel free to
download the code
for PSpec and play with it. It requires a fairly recent version of
Rakudo Perl 6.