-
Notifications
You must be signed in to change notification settings - Fork 83
Step Definitions
##@Dev
In Cucumberish test cases are written as features and each feature consist of one or more scenarios and each scenario is built up by set of steps. All you need to consider is to make sure that any step that is written in any scenario has a matching implementation.
Implementations are matched using string comparison and Regex. Example:
Given(@"the app is running", ^void(NSArray *args, id userInfo) {
//Implementation body goes here
});
This is a straight text, no regex is going on here. This definition implementation will be called if, and only if, the following step exists in any scenario
Give the app is running
You can also define more flexible matching implementation using the full power of regular expression matching. Which means, your step implementation will also have access to variable number of arguments
Note: if you are not familiar with regular expression you will find the below examples really difficult while they are not that difficult. Please try to get familiar with it first before moving on. At the end of this wiki page you can find some good references that can get you into it as quick as possible.
Example:
When(@"^I write \"([^\\\"]*)\" (?:into|in) (?:the )?\"([^\\\"]*)\" field$" , ^void(NSArray *args, id userInfo) {
//This will match several sentence like:
//When I write "Ahmed" in "Name" field
//When I write "Ahmed" into "Name" field
//When I write "Ahmed" into the "Name" field
//When I write "Ahmed" in the "Name" field
//etc
});
With this power of regex matching, your QA colleagues will be happy with the flexibility they have to write very comprehensive steps.
Also very important thing to note about the above example, this example has two capturing groups: first one meant to match the text and the second one meant to match the field name. You access whatever matches this matching group from the args array that is passed to your implementation block:
When(@"^I write \"([^\\\"]*)\" (?:into|in) (?:the )?\"([^\\\"]*)\" field$" , ^void(NSArray *args, id userInfo) {
//When this implementation is called because it matched the following step
//When I write "Ahmed" in "FieldName" field
//Then args[0] will be equal to Ahmed and args[1] will be equal to FieldName
});
In Cucumberish you define your step implementation in the code before you call -[Cucumberish beginExecution] method. There are five C functions for that:
void Given(NSString * definitionString, CCIStepBody body);
void When(NSString * definitionString, CCIStepBody body);
void Then(NSString * definitionString, CCIStepBody body);
void And(NSString * definitionString, CCIStepBody body);
void But(NSString * definitionString, CCIStepBody body);
Each definition can only match a step that has the same preposition and a matching string; E.g starts with the same word as the function name and the rest of the step matches whatever string you pass as your definitionString. The following step definition
Given(@"the app is running", ^void(NSArray *args, id userInfo) {
//Implementation body goes here
});
Will match only the step "Given the app is running", but not "When the app is running".
However, sometime you may want to define a step once and use it in different contexts. Let's say you have the following step definition:
When(@"I write \"(.*)\" into the \"(.*)\" field", ^void(NSArray *args, id userInfo) {
//Implementation body goes here
});
What if you want it to be more flexible and instead of matching only "When I write "blah blah" into the "Fancy" field", you want it to also match the same step but with "Then I write "blah blah" into the "Fancy" field".
For such cases there is two more handy C functions:
void MatchAll(NSString * definitionString, CCIStepBody body);
void Match(NSArray *prepositions, NSString * definitionString, CCIStepBody body);
The MatchAll function will make the same definition available for "When, Then, And, But" steps; Given is left out intentionally because it is very unlikely to have a case when a definition is suitable for Given and any other kind of steps.
MatchAll(@"I write \"(.*)\" into the \"(.*)\" field", ^void(NSArray *args, id userInfo) {
//Implementation body goes here
//And this implementation will be invoked with any of the following steps
//When I write "blah blah" into the "Fancy" field
//Then I write "blah blah" into the "Fancy" field
//And I write "blah blah" into the "Fancy" field
//But I write "blah blah" into the "Fancy" field
});
What if you want to only match steps that starts with "Given and When"? Then you can use the last function Match This function's first parameter is an array of prepositions that you want to match. Example:
Match(@[@"Given", @"When"], @"I write \"(.*)\" into the \"(.*)\" field", ^void(NSArray *args, id userInfo) {
//Implementation body goes here
//And this implementation will be invoked with any of the following steps
//When I write "blah blah" into the "Fancy" field
//Given I write "blah blah" into the "Fancy" field
});
Steps can also include a multi-line argument known as DocString, or table of data known as DataTable. Please consider checking these two very berief pages: DocString and DataTable
With the more steps you implement, you will find yourself repeating the same piece of code inside more than one step implementation; and it is even become worse when you have high level step definition, because a lot of their implementation details will be repeated over and over again.
There are many ways you can tackle that; one of these ways is provided with Cucumberish and it is to call step from within another step. And in such cases, you do not need to worry about the preposition of the step you are calling. Example:
When(@"I tap the \"(.*)\" button", ^void(NSArray *args, id userInfo) {
//Code to tap the button goes here
});
Given(@"I logged in with the user \"(.*)\", ^void(NSArray *args, id userInfo) {
//Fill the user data
//Then tap the Login button, which will require re-using the previous step; and note how we completely ignored the preposition in our call to the Step function.
Step(@"I tap the \"%@\" button, @"Login");
});
Note: Step function is not available in Swift, there is another alias function called SStep.
Since the real XCAssert function supposed to be called from with an XCTestCase class implementation; and this is not available for you in the compile time. You will see a lot of warnings if you used XCAssert. As an alternative, Cucumberish provides the following functions for assertion and throwing exceptions.
Objective-C
void CCIAssert(BOOL expression, NSString * failureMessage, ...);
void throwCucumberishException(NSString *reason, ...);
Swift
func CCISAssert(expression : Bool, failureMessage : String);
func SThrowCucumberishException(reason : String);
###Apendix: Regular Expression references
- Heavily detailed general regular expression reference: http://www.regular-expressions.info
- After you become familiar with the basics, you can keep this cheat sheet in your pocket.
- One important note about regex in Objective-C and Swift; when writing special regex characters like \s, you will have to duplicate the slash, so it ends up like that \\s.
- Also in Cocoa and Cocoatouch regular expression string does not need boundaries; which in short means you do not need to put the regular expression string between two slashes or between any other symbols.
- If you are interested to know more about regex details and implementation in the Foundation framework, Apple documentation is your best friend.