8

I do know that basically all Given, When, Then and And keywords in feature definition technically are not distinguished by Cucumber. But why does Cucumber have different annotations for the step definitions @Given, @When, @Then, and @And?.

What I am observing is either you have the code describing the step:

@When("^first operand = \"(.+?)\"$")
public void setFirstOperand(String firstOp){
    firstOperand = Integer.parseInt(firstOp);
}

or you have the code:

@Then("^first operand = \"(.+?)\"$")
public void setFirstOperand(String firstOp){
    firstOperand = Integer.parseInt(firstOp);
}

both execute the logic of the test in an identical way.

Is there any difference how Cucumber framework treats the code under those different annotations? I believe there should be some difference otherwise this architecture (when you have different entities intended to address the same logic) would not look pretty "object-oriented".

2

It seems that your question was misunderstood several times and not even your comments fixed this situation. I think at least I got your question right - at least I find it quite clear: It's not about the DSL, it's about the annotations for test steps.

Unfortunately, I can't answer why cucumber actually has different annotations for step definitions, but I know why I would have designed the API with those different annotations if I had designed it.

I'm not at all an expert for cucumber but I find the way the semantics of Given, as described in cucumber's Gherkin specification quite contradictory. It starts sensibly:

Given steps are used to describe the initial context of the system - the scene of the scenario. It is typically something that happened in the past.

Perfect! Clearly a precondition, right? Could mean if this precondition isn't fulfilled, the scenario simply doesn't apply.

The next sentence of the same description destroys this hope:

When Cucumber executes a Given step, it will configure the system to be in a well-defined state, such as creating and configuring objects or adding data to a test database.

That's definitely not what we call separation of concerns - I'd rather call it messing up concerns.

For configuring a system to be in a well-defined state we usually have setup tasks with @Before annotations or whatever.

In natural English (I'm not a native speaker so I might be slightly wrong) Given introduces a precondition. And that's exactly where we COULD benefit from a specific @Given annotation (cucumber seems to have missed this chance): it could (and I think should) annotate a boolean function that indicates whether or not the test (all the Whens and Thens as well as further Given conditions) are applied or not.

In simpler words: ideally, tests would never fail if a precondition isn't fulfilled. This would introduce significantly enhanced flexibility regarding test data.

Back to natural language (well, somewhat semi-natural):
"Given you accepted the meeting. When the meeting starts, Then I expect you to be in the meeting room."

Here I'm saying that all the rest only applies if you accepted the meeting. If you didn't the meeting might not start and even if it starts I won't expect you to be in the meeting room.

And this is where the value of a specific @Given annotation could come in: a test runner could ignore all tests or all outline records that don't match the precondition. In a similar manner as JUnit deals with assumeThat.

I could imagine that the designers of the API had something like this in mind. Maybe after the API design was finished, there wasn't enough time to implement it like this, or the people implementing the solution had a different opinion.

Since the test runner obviously isn't implemented in the way the different annotations suggest, I suppose it is what you seem to be perceiving: a design flaw.

Even worse this flaw leaks into the DSL. Because it means that the keywords have no specific semantics either.

And obviously enhanced semantics for the existing DSL and API can't be introduced without potentially breaking existing tests - or even more probable and even worse: hiding errors that existing tests now cover by abusive Given semantics.

Sorry for having wasted so many words for again not answering your question. But at least now you have an answer that addresses your question - and you have found a fellow sufferer.

| improve this answer | |
  • I don't see any value added by this answer except showing sympathetic view towards OP. – Vishal Aggarwal Apr 7 '19 at 3:14
  • 1
    @Vhishal Aggarwal, I'm sorry that you can't see any value added by my answer. As a reparation I'll try my best to find value in your comment. – yaccob Apr 7 '19 at 3:29
  • 1
    @VishalAggarwal OP was asking about the technical differences between the different annotations Cucumber provides, and your answer did not address this. yaccob provided some possible justifications for why there would be multiple annotations that seem to do the exact same thing, which seems to be the best post so far – Vivek Chavda Dec 6 '19 at 19:00
5

The problem Cucumber addresses is communication not architecture.

These keywords are just syntactical sugar, for the business user's understanding and readability. Technically there is no difference.

Having said that, the whole concept of Cucumber (BDD) is to have a common shared vocabulary(DSL) so all team members can communicate on the same level including non-technical people.

The value received is for the whole team in terms of improved communication by a shared vocabulary.

As far as coding is concerned,

A function's job is to return the current state of the application, it's test's job to determine (assert) it is positive or negative based on the given test scenario and pass or fail the test accordingly.

So a function can return the same state which might be determined positive/ or negative by the test script depending on the scenario it's currently validating.

| improve this answer | |
  • 1
    Thanks for your answer! But it seems that you are talking about the feature definition. If we talk about the "users" the approach might make sense, but we're talking about programming framework that is written by coders for coders. The better readability in that case rather means the better structure and adhering OOP programming best practices. As for a programmer it would be more readable if there would be, say, the annotation like "@StepDef(type = EStepType.GIVEN, regexp="..."). – Alexey R. Jul 18 '18 at 9:39
  • The whole concept of Cucumber (BDD) is to have a common shared vocabulary(DSL) so all team can communicate using it on same level including non- technical people. – Vishal Aggarwal Jul 18 '18 at 9:55
  • Yes, but what value do you as a developer get from either the step will be Given or When or Then if there will no be difference in implementation or in logic. This will only increase uncertainty of your code since you will code the step as if it would be Given but the user will be able to use it as if it would be Then – Alexey R. Jul 18 '18 at 9:57
  • 1
    Vocabulary without distinct semantics is not a DSL. And I doubt that sharing 3 words that are arbitrarily interchangeable improves communication. – yaccob Apr 7 '19 at 3:59
  • 1
    I am really sorry if Cucumber specification does not meet your requirements.I understand your concern but Cucumber issue repo would be better place if something needs to be updated/fixed in cucumber. – Vishal Aggarwal Apr 7 '19 at 13:17
1

The cucumber keywords like Given, When, And, But, Then are used to enhance the readability and understand ability of the scenario which the QA is planning to test in the scenario.

The idea of the BDD framework like cucumber is to enhance the knowledge of what is being tested to people outside the testing layer like the BAs and the developers or product owners.

The syntax of a typical scenarios is like as follows:

Scenario: Some determinable business situation

Given some precondition exists

And some other precondition exits

When some action by the actor

And some other action

And yet another action

Then some testable outcome is achieved

And something else have also happened
| improve this answer | |
1

The difference lays in design, not functionality

As there seems to be some dissatisfaction with the accepted and given answers, I want to wrap it up, risking to only add some noise to the thread.

The answer to your question is... only that Then "jada jada" is differentiated from Given "jada jada" and calling another part of your testing code respectively, although sharing the same name (trivial difference).

Jithu Paul wrote appropriately:

The idea of the BDD framework like cucumber is to enhance the knowledge of what is being tested to people outside the testing layer like the BAs [...] or product owners.

It is not meant to primarily (at all?) "bring functionality" in a technical sense (as different keywords in a programming language would) but to "translate" your testing framework to non-technical instances and vice versa (e.g. support tickets etc). The mentioned keywords only aim to deliver syntactical sugar to make it readable "as if it were English".

That said I want to show an example of how I differentiate between them:

  • Given describes preconditions (e.g. "I am a logged in User") - .. and therefore may involve many steps, like navigating to the log in form, filling in values, clicking a button, etc. This precondition comes with an expectation that could (should?) be checked against (~ a final assertion in the function itself).

  • When describes a single Step (e.g. "I click on the articles link") - .. and thus is not primarily part of the tested functionality. I rarely check them against expectations (as I would test code twice often). The main difference to the Given steps is that When steps are much more trivial. I put here everything that is crucial for the current feature only to not end up repeating myself.

  • Then steps validate an expectation (e.g. "I should see a list of articles) - .. and that means they are not at all as the prior two: They return true and false (instead of an action) and determine the progress and quality of your code "to the outer world".

Obviously, all the keywords do the same: executing (any) code. You could(and should not) write your tests using only one of them. (Again: Gherkin is written for non technical personas)

Finally, if you treat the steps as suggested, you Don't Repeat Yourself, and have a much more readable and maintainable codebase (which is important for developers to work fast and clean).

If you do view and controller testing for your own project as a developer, I would not suggest you to use cucumber at all, instead, use a more technical framework to lay out your specs. (I don't come from a Java perspective here, as I used cucumber only for ruby so far, but the principles are the same.) Under the hood, Cucumber uses another language, and maybe frameworks. Use them since you are familiar with them, unless you need to translate your specs, or to give non technical people the option to write their own (that you then had to implement).

| improve this answer | |
  • 1
    Not a noise at all but really valuable answer. Thanks! – Alexey R. Apr 8 at 15:44
0

Typically you setup your scenario in the given step, this could be connecting to a service, loading its configuration, priming its fixtures

In the when step you have the principal action you are testing, this could be a user request or a service taking an action.

You could chain actions with and steps.

Finally the then step will verify the action occurred, this can be by asserting about the resulting data or state.

With good step design, you can reuse steps between features to increase coverage and efficiency.

This is best achieved with careful parameterization of the steps to make them extensible.

You can also use tables in your features to run a step multiple times with different data from the table rows each time.

You can also run the same steps with values hardcoded in to the feature lines.

Other than looping if you use tables there is no difference in how those functions are invoked by cucumber than any other. At least in most implementations I've seen but it may be different in yours. You could run it in a debugger and check.

Some custom tools like SpecFlow pass around all sorts of other objects and state to allow you to construct the scenario you are testing.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.