Storyteller was largely envisioned as a replacement for the older FitNesse tool. One of the aggravations that the Storyteller team had when using FitNesse years ago was the sheer repetiveness of having to repeat the same basic steps in different specifications. To alleviate that pain, Storyteller was designed from the very beginning to support the idea of "Composite" grammars that allowed you to specify a series of steps that have to be performed together and possibly in a certain order.
The Paragraph grammar can include any number of grammars of all types. A simple example is entering credentials into a login screen. You typically need to enter a user name and a login, then click a login button or hit enter to submit the credentials. We can use a Paragraph grammar to batch up the single logical operation from individual grammars like so:
[Hidden, FormatAs("Enter the user name {user}")]
public void EnterUserName(string user)
{
}
[Hidden, FormatAs("Enter the password {password}")]
public void EnterPassword(string password)
{
}
[Hidden, FormatAs("Click the login button")]
public void ClickLoginButton()
{
}
public IGrammar Login()
{
return Paragraph("Enter login credentials", _ =>
{
_ += this["EnterUserName"];
_ += this["EnterPassword"];
_ += this["ClickLoginButton"];
});
}
In usage, the grammar above looks like this:
The grammars added to the paragraph can refer to other grammars in the current Fixture or grammars built on the spot. The code below is a sample of this from Storyteller's own integration tests:
public IGrammar Divide()
{
return Paragraph("Divide numbers", x =>
{
x += c => _first = _second = 0;
x += Read<double>("x", o => _first = o);
x += Read<double>("y", o => _second = o);
x += Check("quotient", () => _first/_second);
}).AsTable("Do some division");
}
Silent Grammars
It's frequently useful to execute some kind of operation before, after, or between the individual grammars within a Paragraph. Storyteller exposes the idea of a Silent Grammar for exactly this usage. The following sample rebuilds the Paragraph grammar from above, but this time using a silent action to click the login button:
public IGrammar Login2()
{
return Paragraph("Enter login credentials", _ =>
{
_ += this["EnterUserName"];
_ += this["EnterPassword"];
// Lastly, a silent action
_ += c =>
{
// supply a Lambda that would click
// the login button
};
});
}
Silent grammars can be added to a Paragraph with the signatures:
Action
Action<ISpecContext>
-- if you need information about the current specification, access to the execution state, or want to resolve services from the system under test.