Here's a pre-processor for Cucumber An explanation of its purpose is here:
https://github.com/atdd-bdd/preprocessor/blob/master/Cucumber%20Preprocessor%20Introduction.pdf
It uses #define and #include directives to transform feature files in one directory into processed files in another directory. It stands separate from Cucumber and is executed prior in the build to running cucumber. I'm trying it out as an experiment. One feature is the ability to include csv files as tables. Someone had mentioned that a few years ago in a separate thread.
When value is over the 200 maximum
Then report error
Given a maximum of 200
When the value is over the maximum
Then report error
Given maximum value is set to 200
When the user sets a value above the maximum
Then an error is reported
When the dataset matches the expected content
When the dataset matches the list of "banned users"
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Roberto,
Thanks for your reply. We have different approaches to what a feature file represents and how it is used. If a feature file does not contain any data, then there is no need for this preprocessor, as you illustrated. The more data one places in a feature file, then the possibility of values that represent one domain term can be duplicated. Having #defines for those terms can make for easier maintenance. In addition, giving names to values helps create ubiquitous terms for understanding for the entire team (e.g. MAXIMUM_VALUE).
I've found that having specific values in a scenario really helps in discussion between the business and development. Using tables to contain these values reduces the number of step definitions that need to be created.
As far as CSV files, see my response to Halfordian Golfer.
Roberto,
I think I see where a misunderstanding lies.
I am experimenting with Gherkin to become a more useful tool for testing as well as communication. One of the original purposes of Gherkin was as an alternative to JUnit.
See https://dannorth.net/introducing-bdd/
Given I've signed upI can log inBut only after I've confirmed my email address
assertNotNull(username)assertNotNull(password)assertTrue(emailConfirmed)assertEquals(username, storedUsername)assertEquals(hash(password), storedPassword)
Take a look at my Deliver Agile Session (you need to login as an agile alliance member) https://lnkd.in/enCkUVf
There are two advantages:of using Gherkin
1.) The test is independent of the implementation. It specifies the behavior of a unit.
2.) The test is readable by all members of the triad (customer, developer, tester).
Ken
On Friday, June 15, 2018 at 5:20:49 AM UTC-4, Roberto Lo Giacco wrote:Il giorno giovedì 14 giugno 2018 16:22:23 UTC+2, kpughc...@gmail.com ha scritto:Roberto,
Thanks for your reply. We have different approaches to what a feature file represents and how it is used. If a feature file does not contain any data, then there is no need for this preprocessor, as you illustrated. The more data one places in a feature file, then the possibility of values that represent one domain term can be duplicated. Having #defines for those terms can make for easier maintenance. In addition, giving names to values helps create ubiquitous terms for understanding for the entire team (e.g. MAXIMUM_VALUE).
I've found that having specific values in a scenario really helps in discussion between the business and development. Using tables to contain these values reduces the number of step definitions that need to be created.True, but that could be achieved in a non programmer way:BackgroundGiven the system is set up for with| param | value || maximum | 200 || minimum | 50 || min length | 6 |When the user inputs a value above the maximum allowed valueThen an error is shownYou should now question yourself: is what is written above a functional requirement or a test? If your answer is the former within your context, than you are ok. If it's the latter than why bother use Gherkin?
As far as CSV files, see my response to Halfordian Golfer.My take on that is if I need a CSV to describe a business rule then that business rule doesn't require Gherkin. I would also question myself why I call "business rule" a CSV file and if I'm misinterpreting the concept of business rule if I am thinking as a developer, trying to squeeze business rules in a tabular format rather than trying to describe the business rule for what it is... Does a table communicate better than natural language? We, as software developers, are used to communicating in ways our business partners do not understand, but if we don't treat Gherkin as a tool for bridging communication between those two parties, why should we bother?Regards,Roberto
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to a topic in the Google Groups "Cukes" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cukes/F0AQ0DpVoZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cukes+un...@googlegroups.com.
Ken,
On 6/14/18 2:00 PM, kpughc...@gmail.com wrote:
> George,
>
> So here's an alternative using the proposed #include.
>
> Business person updates the spreadsheet and saves it as a CSV file
> in a shared location.
> Build script copies the CSV file and checks it into source
> repository. (If business person could easily update source
> repository, this could be combined with previous step)
> Preprocessor run, inserts the file, then Cucumber runs.
> Output shows results.
After running the preprocessor, do you end up with a normal gherkin
feature file? If so, that seems like an easy way to let the business
people edit the part of the gherkin they want to edit in a tool that is
familiar to them. I suggest versioning the processed file as the
canonical description of the functionality.
George,
I'm still exploring potential patterns. However some of the things I have seen in the past are:
Business Rule Pattern
Business person creates a spreadsheet that contains a table representing a requirement (a business rule or tests for that business rule). Developers copy and paste that table into a feature file as a table (typically an example table). Now the business person updates the values. The developers need to update the feature file manually.
One alternative used was to read the spreadsheet into the test via an Excel library. Changes to the spreadsheet would be then incorporated into the test. The code to read the Excel spreadsheet was a bit of work.
So here's an alternative using the proposed #include.
Business person updates the spreadsheet and saves it as a CSV file in a shared location.
Build script copies the CSV file and checks it into source repository. (If business person could easily update source repository, this could be combined with previous step)
Preprocessor run, inserts the file, then Cucumber runs.
Output shows results.
Domain Term Pattern
I've also seen a lot of duplication with values in tests, such as:
When value is greater than 200
....
When value is equal to 200
Here's the alternative with #define:
#define MAXIMUM_VALUE 200
When value is greater than MAXIMUM_VALUE
...
When value is equal to MAXIMUM_VALUE
This is equivalent to using named constants in code, which makes code much easier to maintain. In addition, it creates a domain term - MAXIMUM_VALUE that is shared among the team and appears in the feature file for documentation.
Roberto,We do have different perspectives.
I've found Given/When/Then (similar to Arrange/Act/Assert, PreConditions/Action/PostConditions, etc.) to be extremely useful in expressing requirements/tests. In one sense, the only difference between a requirement and a test is that the Then part checks the actual result to the expected result.
Given/When/Then can have varying levels of expression:Here's an generic one. It really doesn't have enough in it to be a test.Given a calculatorWhen I add two numbersThe sum is correctHere's a specific example, which is found during discovery phase This could be used as a test.Given a calculatorWhen I add 2 and 2The sum is 4Here's a specific example, which also documents domain terms (e.g. addend)Given a calculatorWhen I add <addend1> and <addend2>Then the sum is <sum>Examples:|addend1 |addend2 |sum||2 | 2 | 4 |If the Given/When/Then is in this form, then it's ready for step defs to turn in into an automated test. But it's also in the language of the customer. So it has a dual purpose. Expressing the requirement and being the test for the requirement. And a tester can easily add additional examples to check on boundary conditions or edge cases without having to do any copy and paste.
Ken
Here's a link to the PDF of the presentation: :
Ken
On Wed, Jun 20, 2018 at 1:46 AM <kpughc...@gmail.com> wrote:Here's a link to the PDF of the presentation: :Thanks a million for sharing Ken.Sorry, but I once again have to disagree: using Cucumber for unit testing is a certain path toward failure... I say that for personal experience.
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+un...@googlegroups.com.
On Tuesday, June 19, 2018 at 8:25:23 PM UTC-4, Roberto Lo Giacco wrote:On Wed, Jun 20, 2018 at 1:46 AM <kpughc...@gmail.com> wrote:Here's a link to the PDF of the presentation: :Thanks a million for sharing Ken.Sorry, but I once again have to disagree: using Cucumber for unit testing is a certain path toward failure... I say that for personal experience.And from personal experience as well, I'm going to have to disagree with you on that, Roberto. There are times when writing unit tests in a business readable format (e.g. Gherkin) is totally appropriate. They aren't as common as non-unit business readable test but, none the less, they exist. It turned out quite well.On a related note: http://claysnow.co.uk/the-testing-iceberg/
Enjoy,EricThe developers will hate you because you are forcing them to write and maintain doubled files (one for the feature, one for the stepdef)The QAs will hate you because they will have to maintain a lot of files (that explains your #include and #define to me now)The Project Manager will hate you because you are spending a lot more effort in testingThe Product Owner will hate you because you will be forcing him to read and validate tons of files he doesn't really care of (or he will just nod at you while you go through all of them)Did you really manage to have "los tres amigos" work on those together? Sorry buddy, I can't believe that...Trust me, you'll do yourself a favour if you don't use Cucumber for unit testing: it is truly not meant for that.Another certain path toward failure is trying to generalize stepdefs: that's another error I've made and paid on my own skin.... Don't try to reduce the number of stepdefs so to have "only one stepdef to click a button": you'll end up having very complex and costly to maintain stepdefs. But that's another story, may be worth another blog post :-D Please note that post has been there since January 2014...RobertoKen
Roberto,As I mentioned, we differ on perspectives.
Here are an article on writing tests for business rules using an ATDD/BDD framework:
My approach is that if business rules are important, then having business readable tests for those rules is also important. Using developer-centric tools like JUnit does not make those tests business readable.
Now our views are shaped by our context and experience. We have different contexts and experiences. So it's no surprise we have different views that are equally valid.
One of the reasons for using #defines is to clearly indicate domain terms (as in Domain Driven Design) which should be global as you mentioned. As George suggested, he didn't like the term MAXIMUM_VALUE, he would rather have it be MAXIMUM_ALLOWED_VALUE. By agreeing on the term and using it everywhere, then misunderstanding decreases.For example, one might write two scenarios containing:When value is less than maximum valueWhen values is greater than maximum allowed valueThen one might ask whether these are referring to the same domain term and simply expressed with different text.With #defines, scenarios would look like:When value is less than MAXIMUM_VALUEWhen values is greater than MAXIMUM_ALLOWED_VALUEThe terms stand out.
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+un...@googlegroups.com.
Here's a pre-processor for Cucumber An explanation of its purpose is here:
https://github.com/atdd-bdd/preprocessor/blob/master/Cucumber%20Preprocessor%20Introduction.pdf
It uses #define and #include directives to transform feature files in one directory into processed files in another directory. It stands separate from Cucumber and is executed prior in the build to running cucumber. I'm trying it out as an experiment. One feature is the ability to include csv files as tables. Someone had mentioned that a few years ago in a separate thread.
--
Let me re-focus this discussion around the pre-processor, not about other issues.
3.) Different format for #defines.e.g.#definevalues (or something similar)| MaximumAllowedValue | 200 || MinimumAllowedValue | 100 |
You received this message because you are subscribed to a topic in the Google Groups "Cukes" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cukes/F0AQ0DpVoZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cukes+un...@googlegroups.com.
The Cucumber logo is the intellectual property of Cucumber Ltd, a limited company registered in Scotland, number 456793.
Registered address: Cucumber Ltd, The Melting Pot, 5 Rose Street, Edinburgh EH2 2PR, Scotland, United Kingdom.
@Ken I’m wondering, would one way of solving this problem be to add a new keyword to Gherkin that acts like a global Background, say we call it Define? You could use it to specify global terms like in Gherkin this which could then be used by steps elsewhere:# in Gherkin (somewhere)
Define:Given the Maximum Allowed Value is 500
I see this list growing pretty fast, with increasing risk of naming collisions and misuse…
The Cucumber logo is the intellectual property of Cucumber Ltd, a limited company registered in Scotland, number 456793.
Registered address: Cucumber Ltd, The Melting Pot, 5 Rose Street, Edinburgh EH2 2PR, Scotland, United Kingdom.
CONFIDENTIALITY NOTICE: The information in this e-mail is confidential and privileged; it is intended for use solely by the individual or entity named as the recipient hereof. Disclosure, copying, distribution, or use of the contents of this e-mail by persons other than the intended recipient is strictly prohibited and may violate applicable laws. If you have received this e-mail in error, please delete the original message and notify us by email immediately. Thank you. Cucumber Ltd.
--
@Ken I’m wondering, would one way of solving this problem be to add a new keyword to Gherkin that acts like a global Background, say we call it Define? You could use it to specify global terms like in Gherkin this which could then be used by steps elsewhere:# in Gherkin (somewhere)Define:Given the Maximum Allowed Value is 500
On 23 Jun 2018, at 04:05, kpughc...@gmail.com wrote:
On Thursday, June 21, 2018 at 5:51:19 AM UTC-4, Matt Wynne wrote:@Ken I’m wondering, would one way of solving this problem be to add a new keyword to Gherkin that acts like a global Background, say we call it Define? You could use it to specify global terms like in Gherkin this which could then be used by steps elsewhere:# in Gherkin (somewhere)Define:Given the Maximum Allowed Value is 500That's a possibility, particularly if the missing step def creator did the creation of @maximum_allowed_valueAnother possibility could be:Define Maximum Allowed Value is 500The question is when would the substitution be made. It could be thatGiven the value is less that the Maximum Allowed Valuematches the step defi
"the value is less that the Maximum Allowed Value"or it could match a step def with a parameter :"the value ish less that the %d"Ken
Inspiring meaningful, effective collaboration in every software delivery organisation.
The Cucumber logo is the intellectual property of Cucumber Ltd, a limited company registered in Scotland, number 456793.
We have a new thing in Cucumber called parameter types[1] which, if these Define steps became simply global Givens, could I think be used to set up a placeholder that would then be substituted in whether the name of the domain term was used.Ken, do you think it’s worth us turning this specific idea (a global Background) into a ticket on the Gherkin repo for further discussion there?
ParameterType( name: 'MaximumValue', regexp: /(MaximumValue)/, transformer: -> (identifer) { 200 } )
And then:
When value is less than MaximumValue
would have MaximumValue replaced by 200
Here's another example following along the previous ideas.
#defines (or Define if a Gherkin keyword)
| SomeOne| "Bill" |
| SomeOneElse | "Jane" |
| ValidMessage | "Hi" |
| DistanceInRange | 100 |
| MaximumDelay | 2 |
Scenario: Shout Out
Given that SomeOne and SomeOneElse are in DistanceInRange
When SomeOne shouts a ValidMessage
Then SomeOneElse sees ValidMessage before MaximumDelay
This abstracts out a specific example into a generic one.
The preprocessor would transform it back to a specific one.
The question is whether having the specific values be available in the feature file or having them in the glue code would be preferable.
If you think putting this on the Gherkin repo would be a good idea, I'll do it. I was keeping things as a preprocessor for more experimentation purposes.
Ken
#defines (or Define if a Gherkin keyword)
| SomeOne| "Bill" |
| SomeOneElse | "Jane" |
| ValidMessage | "Hi" |
| DistanceInRange | 100 |
| MaximumDelay | 2 |
Scenario: Shout Out
Given that SomeOne and SomeOneElse are in DistanceInRange
When SomeOne shouts a ValidMessage
Then SomeOneElse sees ValidMessage before MaximumDelay
On Monday, June 25, 2018 at 9:52:16 AM UTC-4, Matt Wynne wrote:We have a new thing in Cucumber called parameter types[1] which, if these Define steps became simply global Givens, could I think be used to set up a placeholder that would then be substituted in whether the name of the domain term was used.Ken, do you think it’s worth us turning this specific idea (a global Background) into a ticket on the Gherkin repo for further discussion there?That's an interesting idea. As understand them to work from that description, one could have:ParameterType( name: 'MaximumValue', regexp: /(MaximumValue)/, transformer: -> (identifer) { 200 } )
ParameterType( name: 'MaximumValue', regexp: /(MaximumValue)/,
transformer: -> (identifer) { @maximum_value } )
And then:
When value is less than MaximumValue
would have MaximumValue replaced by 200
Here's another example following along the previous ideas.
#defines (or Define if a Gherkin keyword)
| SomeOne| "Bill" |
| SomeOneElse | "Jane" |
| ValidMessage | "Hi" |
| DistanceInRange | 100 |
| MaximumDelay | 2 |
Scenario: Shout Out
Given that SomeOne and SomeOneElse are in DistanceInRange
When SomeOne shouts a ValidMessage
Then SomeOneElse sees ValidMessage before MaximumDelay
This abstracts out a specific example into a generic one.
The preprocessor would transform it back to a specific one.
The question is whether having the specific values be available in the feature file or having them in the glue code would be preferable.
I'm not sure how the wire protocol would match the preprocessor constructs. Could you give me an example of using the wire protocol that matches the example:#defines (or Define if a Gherkin keyword)
| SomeOne| "Bill" |
| SomeOneElse | "Jane" |
| ValidMessage | "Hi" |
| DistanceInRange | 100 |
| MaximumDelay | 2 |
Scenario: Shout Out
Given that SomeOne and SomeOneElse are in DistanceInRange
When SomeOne shouts a ValidMessage
Then SomeOneElse sees ValidMessage before MaximumDelay
You received this message because you are subscribed to a topic in the Google Groups "Cukes" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cukes/F0AQ0DpVoZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cukes+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Well, kinda. I more thought that you'd want to keep the 200 on the surface in the Gherkin, so something more like:ParameterType( name: 'MaximumValue', regexp: /(MaximumValue)/, transformer: -> (identifer) { @maximum_value } )Given(/the MaximumValue is {int}/) do |max|@maximum_value = maxend
I really like the idea of a Define keyword. It might allow bothDefine MaximumAllowedValue 200andDefine:|MaximumAllowedValue|200||SomethingElse| 300|
ParameterType( name: 'Dollar', regexp: /(\$?\d+\.?)/, transformer: -> (identifer) { new Dollar(identifier) } )
Ken
Ken
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to a topic in the Google Groups "Cukes" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cukes/F0AQ0DpVoZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cukes+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
On 2 Jul 2018, at 17:23, kpughc...@gmail.com wrote:
On Saturday, June 30, 2018 at 10:42:02 AM UTC-4, kpughc...@gmail.com wrote:I really like the idea of a Define keyword. It might allow bothDefine MaximumAllowedValue 200andDefine:|MaximumAllowedValue|200||SomethingElse| 300|Just thought of another thing that could be added that goes along with the Parameter Types. Suppose there was one like:ParameterType( name: 'Dollar', regexp: /(\$?\d+\.?)/, transformer: -> (identifer) { new Dollar(identifier) } )Define MaximumAllowedValue is 200 as DollarorDefine:|MaximumAllowedValue | 200 | Dollar ||SomethingElse | 300 | Dollar |Now the domain terms can have formatting applied to them to make sure they match up to the expected parameter type when used.Yep that’s pretty much how parameter types are intended to be used!To be clear, I’m thinking we would be best to add Define: as another keyword a lot like Background:, but applying to all scenarios in your whole suite.
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+un...@googlegroups.com.
>
> I’m not very keen on adding more “programmer” features to Gherkin.
> That’s been my stance for 10 years now.
I thank you for that. I wonder if this is something that would
communicate with business users. It's awfully easy to accomplish the
same results in the code level, using a step definition to retrieve the
constant from the name.
E.g.,
When the customer withdraws the maximum amount
can be handled by a step definition that looks up what is the current
value defined for "maximum amount" and use that.
There are at least three ways that domain terms and their values could be visible in feature files. Here are some facets of each as I see them.
Preprocessor
#define MaximumAllowedValue 32
When amount is greater than MaximumAllowedValue
or
#define
| MaximumAllowedValue | 32 |
When amount is greater than MaximumAllowedValue
Code
Background:
Given Defines are:
| MaximumAllowedValue | 32 |
The corresponding step def saves away these strings in a DefineCollection.
A step def that wants to use them would change from:
@Given (“^amount is greater than (\\d+)$")
void StepDefinition(int value){
To
@Given (“^amount is greater than (\\w+)$")
void StepDefinition(String inputValue){
int value = DefineCollection.getAsInt(inputValue);
New keyword
Define:
Given the MaximumAllowedValue is 32
with step definitions containing:
ParameterType(
name: 'MaximumAllowedValue',
regexp: /(MaximumAllowedValue)/,
transformer: -> (identifer) { @maximum_value }
)
Given(/the MaximumAllowedValue is {int}/) do |max|
@maximum_value = max
end
with step definitions containing:
ParameterType(
name: 'MaximumAllowedValue',
regexp: /(MaximumAllowedValue)/,
transformer: -> (identifer) { @maximum_value }
)
Given(/the MaximumAllowedValue is {int}/) do |max|
@maximum_value = max
end
- Requires ParameterType for each value
--