In a previous post I introduced the basics of Lucid. This post, and a few follow-ups, will be all about putting Lucid through its paces. If you have a Ruby system all set up, you just need to install the lucid gem. You can read the Setting Up Lucid instructions for more details.
As I noted a bit in the first post, you’ll see that Lucid borrows a lot from Cucumber. Why? Because what Cucumber does works. By way of related example, when Bioware decided to come out with the MMO Star Wars: The Old Republic they made much of it look and operate like the hugely popular World of Warcraft. There were good reasons for that, not least of which was to make the transition easier for players. I’m taking the same approach with Lucid.
Getting Started with Lucid
To get started, create some project directory that you can play around in. Now at the command line, in that directory, type:
$ lucid
Here I’ll use the $ as a sort of generic command prompt.
When you do this, Lucid will essentially tell you that it cannot find a ‘specs’ directory. Here’s an area right at the start where Lucid differs from Cucumber. Instead of a ‘features’ directory, there is a ‘specs’ directory that is expected by default. This isn’t to say you can’t use a ‘features’ directory. It’s simply to say that in Lucid’s opinionated view, what you are writing are specifications — not features. Those specifications will talk about features, yes. But they are not, themselves, features. They are specifications.
A Spec Repository
Okay, so, Lucid requires a spec repository; it defaults to looking in a directory called ‘specs’ for this. That’s easy: just create the necessary directory:
$ mkdir specs
If you run the ‘lucid’ command again, you will be told that Lucid recognizes there is a specs directory but it will do so largely by telling you that it was attempting to find some scenarios and steps to execute. There are none at the current time, of course, so Lucid will simply report that and stop. You should see something very much like this:
0 scenarios 0 steps 0m0.000s
Incidentally, if you do want to use a features directory, simply create one. Then tell Lucid to look in that directory:
$ mkdir features $ lucid features
Here you have specified a spec repository to look in, called ‘features’, and so Lucid will use that rather than the default.
A Specification File
To get the ball rolling a little further, let’s create a specification file. Within your specs directory, create a plain-text file called triangle.spec. Going with the change of ‘features’ to ‘specs’, here the file extension is ‘spec’, not ‘feature’. Again, this is an area where Lucid differs a bit from Cucumber. These are specification files — specifically, test specifications — and are named accordingly.
When you have this file in place, put the following header statement at the top of it:
Ability: Triangle Calculation
Now run the lucid command again. You’ll know that Lucid is finding and reading your spec file because it repeats the header in the output.
Lucid simply reads in text files. You can type the above specification in via Notepad, vi, TextMate, pico, EditPad Pro and so forth. You can even enter it in something like Microsoft Word as long as you save as a text file. As I mentioned in the introductory post to Lucid, I’m using the Gherkin API, similar to tools like Behat, Cucumber, and SpecFlow. Gherkin provides a structuring element for the Test Description Language (TDL) you use to write Lucid specifications. Gherkin is also a library that you can use to parse those specifications.
Note that the header clause “Ability” can also be “Feature”. A spec file is just a file that Gherkin will be able to parse and it’s Gherkin that allows you to use Ability as synonymous with Feature. You can also use the term “Business Need” as well.
So, with that, you are seeing the first organizing principle: Lucid specs are grouped into features or abilities or business needs. These names are used because you want these spec files to describe the features that will be provided, the abilities that users will have, or the business needs that are being satisfied.
Incidentally, if you do want to name your files with a “.feature” extension, rather than “.spec”, you can certainly do that. You just have to tell Lucid that’s what you want to use as the spec type, as follows:
$ lucid --spec-type feature
Let’s digress for a second here. If you have decided to use a ‘features’ directory rather than a ‘specs’ directory and you also want to use a ‘.feature’ file rather than a ‘.spec’ file, you now have two things you have to specify on the command line:
$ lucid --spec-type feature features
If you end up having even more things to specify, that can get annoying to have to type in every time. You can use a configuration file to specify these configuration options. You can create a file called lucid.yml in your project directory and then put the following in that file:
1 |
default: --spec-type feature features |
Were you to do that, you could then just run the lucid command without any arguments as you have been doing:
$ lucid
In this case, Lucid will see that there is a lucid.yml file and use it to determine an operational profile, which is basically just saying a way to operate during execution. Since you have a default configuration, or profile, specified, that is what will be used by … well, by default. And, yes, lucid.yml is basically just like cucumber.yml.
The goal was to make Lucid work out of the box without complex configuration. Lucid doesn’t require you to provide a configuration file or pass a lot of complex command-line options to work. However, there are times when you will want to tweak how Lucid behaves. The configuration options, either at the command line or in a configuration file, are how you will do that. This is somewhat similar, if not identical to, the idea of “convention over configuration” that is favored by frameworks like Rails: if you do everything as the framework “wants” you to, you don’t have to configure much. If you want to go outside those conventions, then configuration options are available to you.
All this being said, for the rest of this post, I’m going to assume you’re not using a ‘features’ directory or a lucid.yml file. I’m going to assume you have the following structure based on what we’ve done so far:
. |---specs triangle.spec
Now let’s add some narrative or a description to this ability.
Ability: Triangle Calculation As a math teacher I need a triangle calculator so that I can have students in my classroom learn about triangles The possible triangle types are: - Scalene: all side lengths are different - Isosceles: two side lengths are equal - Equilateral: all side lengths are equal Invalid side lengths will not form a valid triangle. A "right" triangle has one angle which is 90 degrees. This means the side lengths form a Pythagorean triple and thus satisfy the Pythagorean theorem.
You’ll probably recognize the first part of this — the “As a … I need … so that …” — as a standard feature injection template for indicating why something has value. The rest is just text that a hypothetical business user may have put in to make sure everything is clear.
All of this information is just for the consumption of the reader of this test spec. What you can see is that if you run the lucid command again, all of this information is parroted back as part of the execution of the spec. You might wonder why. Well, the narrative is meant to serve as a context for the test and that context may be very important when the results are looked at. However, Lucid takes no action based on the narrative. As I stated, it simply parrots it back to you. In fact, at this point, that’s all Lucid can do: simply parrot back the spec file it found. Nothing is actually being executed. This is because you do not yet have an executable spec. A spec becomes executable when it has steps within scenarios.
Incidentally, there are a few other ways you can execute Lucid to have it tell you what it’s doing as it is executing. Try the following commands:
$ lucid --verbose $ lucid --debug
The verbose option, at this point, simply tells you this:
[ LUCID (INFO) ] Driver File Found: [] [ LUCID (INFO) ] Runtime Load Execution Context: [] [ LUCID (INFO) ] Orchestrator Load Files: [ LUCID (INFO) ] [ LUCID (INFO) ] Specs: [ LUCID (INFO) ] * specs/triangle.spec [ LUCID (INFO) ] Parsing spec files took 0m0.009s
It’s not all that helpful right now. However, when your spec repository contains may more files, some of which will be used for execution purposes, the verbose output can help. The debug output is only necessarily, not surprisingly, if you need to debug some aspect of Lucid’s operation. In order to operate, Lucid has to essentially construct a lot of objects at runtime. The debug option lets you see what that runtime creation ends up being.
Along with the above commands, Lucid has a fairly rich set of command line options that you can use to control execution. To see these, do this:
$ lucid --help
An Executable Specification File
To start making this specification executable let’s first add a scenario. Change the triangle.spec file so that it looks like this:
Ability: Triangle Calculation ... Scenario: Scalene
You can choose whether to keep the narrative elements or not. (That’s what the ellipsis above replaces.) Here I have added a scenario and given it a title. Run lucid again at the command line.
Now that you have a scenario, the output will show you that Lucid found it and executed it, saying it passed. You’ll even see it in green if your console supports color output. This might seem a little odd. What passed, exactly? A key thing to understand is that Lucid defaults to everything passing. The reason for this is because the onus is on the person writing the test specifications to specifically indicate what failure looks like. Lucid has no way to know that.
You can see, by the output you receive, that passing means little since no actual steps were executed. Now let’s add a step. We know this is a triangle calculator and we’re going to need the lengths of the sides of a triangle. That’s pretty much the context for any actions we take to see if we get back the appropriate triangle. Change your spec file so that it looks like this:
Ability: Triangle Calculation ... Scenario: Scalene Given the length values 10, 8, 3
Note here that I’m showing a lot of indentation to make things look nicer visually. In reality, none of the indentation is needed. Lucid, via Gherkin, line-based, which means each full line is parsed for validity. However, those lines have no layout restrictions in terms of visual placement or indentation.
Here I have added a specific step, using a Given clause. Again, since Lucid is using the Gherkin API, the Given-When-Then format is what Lucid will be counting on. Run lucid again at the command line and this time you will get quite a bit more output. What the output will be telling you is that Lucid is now trying to execute your spec because it has found a step. Notice that the output says there was a step found, but that it was “undefined.” This means that Lucid has parsed your step but has no knowledge of what it means; Lucid doesn’t know how to execute the step. Put another way, Lucid has no idea how to match the natural language step with any sort of code.
You can see via the output that Lucid is helping you write the matchers that you will need in order to start making those steps executable.
As a side note, at this point you will see a message where Lucid is saying that no “test definitions” were found. When Lucid mentions “test definitions” what it’s saying is: I need something that tells me how translate the plain English steps in the spec into logic that I can execute. Those “matchers” I just mentioned will essentially point to the test definitions.
You can see that Lucid is providing a lot of information with your last output. Two command line options will allow you reduce the amount of information if you want. Try the following:
$ lucid --no-source $ lucid --no-matchers
With the no-source option, instead of this:
Scenario: Scalene # specs\triangle.spec:19 Given the length values 10, 8, 3 # specs\triangle.spec:20
you will have this:
Scenario: Scalene Given the length values 10, 8, 3
The no-matchers option pretty much does what it sounds like: it suppresses Lucid’s output of matchers.
Since people often want to run both of those options simultaneously for quick output, you can also do that using the following single option:
$ lucid --quiet
Matchers and Test Definitions
We need a place to put those matchers. So now create a file in the specs directory called triangle_steps.rb.
Incidentally, it’s not necessary that the test definitions file in any way match the name of the spec file. So the fact that my spec file is called triangle.spec does not mean that the steps file must be called triangle_steps.rb. You can follow that convention if you wish, but Lucid certainly doesn’t enforce it. Similarly you did not have to include the word “_steps” in the name of the file.
Once you create that file you can simply copy and paste the matchers that Lucid provides to you. You could also retype them in yourself if you want. The reason you might do that is if you want to change the matchers around a bit. Going with the simple copy-paste route for now, your triangle_steps.rb file will look like this:
1 2 3 |
Given (/^the length values (\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
Run lucid again at the command line.
The scenario now shows that the undefined step has become “pending.” That means Lucid has found how to sync up the natural language with a matcher. That matcher will enclose a test definition. The ‘pending’ keyword in the test definition matcher stops the step from executing any further.
Flexible Matchers
So let’s close out this post talking about some variations that you can do with the step statement and the matchers.
Variation 1
You can do a literal match on a string.
1 2 3 |
Given "the length values 10, 8, 3" do pending end |
This is a very literal string. What this means is that only a TDL statement with those exact numbers will be matched up.
Variation 2
If you are using a string, you can parameterize the string. You can use a $ sign to indicate placeholders.
1 2 3 |
Given "the length values $side1, $side2, $side3" do |arg1, arg2, arg3| pending end |
Variation 3
You can use a literal regular expression. This is basically like variation 1, except the phrase is in a regular expression delimiter, not a string.
1 2 3 |
Given (/^the length values 10, 8, 3$/) do pending end |
Variation 4
You can use capture groups as part of a regular expression.
1 2 3 |
Given (/^the length values (10), (8), (3)$/) do |arg1, arg2, arg3| pending end |
In this case, the groups are essentially useless because the value is still hard coded.
Variation 5
You can use capture groups that are generic.
1 2 3 |
Given (/^the length values (\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
You’ll note that this is essentially what Lucid provided for you as a suggestion. Here I’m using a shorthand character class. That would be as opposed to using something like the “([0-9]*)” character class. Note also that I’m using the + modifier to mean “at least one.” This would be as opposed to using the * modifier to mean “zero or more”.
Variation 6
This isn’t so much a variation as it is showing how multiple matchers can conflict in the sense that Lucid will find it ambiguous as to which matcher it should use. To see this in action, have variation 4 and 5 in your triangle_steps.rb file at the same time. If you have both in place and run the lucid command you will be that there is an ambiguous match.
Run the following:
$ lucid --guess
You’ll see that Lucid still did not feel safe making a determination. The reason this is happening is because Lucid is designed to not necessarily make very smart guesses since, in reality, you don’t want your tool guessing what to do. In this case, what’s happening is the following matchers are being considered:
/^the length values (10), (8), (3)$/ /^the length values (\d+), (\d+), (\d+)$/
To Lucid those are both a string that has the same literal text with three parameters. Lucid will not check that the first has literal parameters and the second has generic. To see how using the ‘guess’ option might work, change your triangle_steps.rb file so it has the following matchers:
1 2 3 4 5 6 7 |
Given (/^the length values 10, 8, 3$/) do pending end Given (/^the length values (\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
If you run the lucid command (without –guess) you’ll see you get the ambiguous warning. Now run lucid again with –guess. In this case, Lucid will make a guess. Why? Well, consider what Lucid is comparing in this case:
/^the length values 10, 8, 3$/ /^the length values (\d+), (\d+), (\d+)$/
In this case Lucid guesses and choose to go with the first matcher and the reason for that is because here it has two choices: a very specific, literal phrase or a phrase that is parameterized. So Lucid will prefer specificity if and when it can determine it. The more specific and literal the phrase, the more comfortable Lucid feels. However, once capture groups get introduced, Lucid gets a little less comfortable.
Variation 7
Here’s a slight change to variation 5, where I’m using the ? modifier:
1 2 3 |
Given (/^the length values? (\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
This is a simple change but it means the word “value” or “values” will be recognized.
Variation 8
This is pretty much the same as variation 7 but now I’m allowing for multiple phrasings with an alternate
1 2 3 |
Given (/^the (?:length|side) values? (\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
You’ll also note that I’m using a non-capturing group element, specified by the ?: characters. This is because I don’t want to capture whether someone used the term ‘length’ or ‘side’, which is what would happen since they are in parentheses.
Variation 9
This is building off of variation 8 in that here I’m allowing for an optional phrasing:
1 2 3 |
Given (/^the (?:length|side) values?(?: of )?(\d+), (\d+), (\d+)$/) do |arg1, arg2, arg3| pending end |
Here the ? character, by itself, indicates that what comes before is optional and thus not be included. So with this in place, Lucid would recognize if someone said “the side values of 10, 8, 3″. As with the length and side alternate capture group, I have also made sure to make this optional phrase non-capturing. Do note, however, that allowing for optional phrasing like this gets tricky because you then have to account for spaces and keep in mind that those spaces must be optional as well.
Variation 10
This is the same as variation 9 but I removed the end of line anchor, indicated by the $ character.
1 2 3 |
Given (/^the (?:length|side) values?(?: of )?(\d+), (\d+), (\d+)/) do |arg1, arg2, arg3| pending end |
With this, I could match anything at the end of the string. Specifically, I could allow for longer phrases at the end that will simply be ignored. So someone could say “the length values of 10, 8, 3 for my triangle” and the “for my triangle” would simply be ignored.
In this post we got started using Lucid and you’ll have seen that it distinguishes itself from it’s close cousin Cucumber in some ways but, in many other ways, is very similar if not identical. As I indicated above, this is largely by design. Cucumber does a lot of cool things and there is zero point in entirely reinventing the wheel. But I think there is also benefit in being able to strike out with your own solution, varying certain aspects to fit situations.
In the next post I’ll continue on building the triangle example started here.