acuminous / yadda

A BDD javascript library
412 stars 73 forks source link

Reuse scenarios #27

Closed hans-d closed 9 years ago

hans-d commented 11 years ago

For setting up a common structure. Cucumber uses a Background that is run at the start of each scenario.

Would like to see named backgrounds, that I can specify in a scenario: Given background A, and perhaps also backgrounds referring another background (circular references may throw errors).

cressie176 commented 11 years ago

Hi Hans,

I'll have a think about it. I assume the intention is to get to a known point in some workflow without having to repeatedly specify the same block of steps? In the meantime you could probably achieve something like this by passing the yadda object to the scenarios via the context. Then inside your "Background A" step, invoke yadda with some text. e.g.

describe('Yadda', function() {

    it('should interpret backgrounds', function() {

        var library = new Yadda.localisation.English()
            .given('background $id', function(id) {
                this.yadda.yadda(load_background(id));
            })
            .define('Log $id', function(text) {
                console.log(text);
            });
        var yadda = new Yadda.Yadda(library);
        yadda.yadda('Given background A', { yadda: yadda });
    });

    function load_background(id) {
        return ['Log X', 'Log Y', 'Log Z']; // TODO fs.readFileSync...
    };
});

The above doesn't handle circular references though (and probably violates the "Clear not Clever" principle). The better solution would be to extend the FeatureParser to be passed a set of backgrounds and to substitute them, as they are encountered.

hans-d commented 11 years ago

Yes, it certainly is to get to known starting point in a DRY way. But also to specify that same background in the feature file almost like a scenario but only using Given, eg

Feature: X

Background: Generic
 Given a fake zookeeper instance
 With value "X" at "/some/location"

Background: Special  B
 Given starting situation "Generic"
 With value "Y" at "/some/other/location"

Background: Special situation C
  Given value "Z" at "/overhere"

Scenario: A
  Given "Generic" background
  And a client doing ....
  ...

Scenario: B
  Given situation "Special B"
 ....

Scenario: C
  Given "Generic" situation
  With "Special C" situation
  ...

Note that Background could also be named differently, eg Situation, or even perhaps Scenario (but only the givens will be included). The inclusion would be something that can be formulated in the library, and would insert the steps at that point in the scenario.

cressie176 commented 11 years ago

How about adding the Background tag (which should be localisable) into a separate file, eg. common.backgrounds. Then they could be shared by multiple features. I'd create a BackgroundParser to parse the backgrounds and check for circular references, then pass the backgrounds to the FeatureParser. The Feature parser would substitute the background steps automatically when it encountered a matching given step, and return the "expanded" scenario, exactly as if you had written the scenario out in full. I'd update the mocha plugins so the test looked like...

background('./backgrounds/common.background', function(background) {
   feature('./featues/zookeeper.feature', function(feature) {
      scenarios(feature.scenarios, function(scenario, done) {
           var yadda = new Yadda.Yadda(library);
           yadda.yadda(scenario.steps, done);
      })
   })
})

In order to support multiple backgrounds, I'd have the option of passing an array or a finder object whose responsibility is to look for backgrounds based on some internal algorithm (e.g. directory and filename pattern matching).

Would this fit your needs?

hans-d commented 11 years ago

This sounds like something that would fit.

And without having too much knowledge (yet?) of the inner workings, I can imagine that in a second iteration (after your version is operational) some of the background-tagged scenarios can even be included in the feature file itself: on parsing the feature file the tagged scenario is provided to the background parser that adds it to its library, so it can be reused by the feature parser on the following scenarios.

AlbanMinassian commented 10 years ago

The ability to have multiple background is interesting. Here's what I would have like syntax. Negotiable.

Feature: X

# no background tag (in scenario => ``given background@`` ( or ``given background`` ))
# no background tag to support others projects
Background:
   Given a fake zookeeper instance
   With value "X" at "/some/location"

@generic (in scenario => ``given background@generic``)
Background:
   Given a fake zookeeper instance
   With value "Y" at "/some/generic"

@extendback (in scenario => ``given background@extendback``)
Background:
    # import another background in background
   Given background@generic
   With value "Z" at "/some/other/extendback"

Scenario: A
   Given background@
   And a client doing ....
   # X set
   ...

Scenario: B
   Given background@generic
   # Y set
   ....

Scenario: C
   Given background@
   Given background@extendback
   # X, Y, Z sets
   ...

cordially Ami44

cressie176 commented 10 years ago

A few things I've been pondering over...

  1. The syntax should be as natural as possible
  2. The same syntax could be used to include scenarios
  3. It should be possible to include multiple backgrounds / scenarios
  4. Backgrounds should be able to include other backgrounds / scenarios
  5. You might want to include a background for all scenarios in a feature
  6. Backgrounds should support the @Pending annotation (and anything which depends on a pending background / scenario should automatically be marked as Pending)
  7. It might be nice to support aliasing of background / scenarios titles so they read naturally in different contexts
  8. It's difficult to decide how backgrounds should interact with example tables

hospital-admissions.feature

    # Defines a background (like a scenario except that it isn't executed)
    Define: Middleton Hospital

       Given a hospital called Middleton
       with a 20 bed, male, cardiovascular ward called Summersham
       and a 15 bed, female, respiratory ward called Bucklesham

    Feature: Hospital Admissions

       # Included for all scenarios in this feature
       Background: 
          Middleton Hospital

       Scenario: Admit a patient

          Given a cardiovascular patient, Mr. Bob Hollness requires admission
          When Mr. Hollness is admitted to bed 1 on Summersham ward
          Then the bed becomes occupied

       Scenario: Attempt to admit a patient into a full ward

          Given a cardiovascular patient, Mr. Bob Hollness requires admission
          but Bucklesham ward is full
          Mr. Hollness cannot be admitted into Summersham ward     

       Scenario: Suggest alternative ward

          # Included for just this scenario
          Background: 
             Attempt to admit a patient into a full ward

          When the Admissions clerk requests an alternative ward
          Then they are offered bed 2 on Summersham ward

       Scenario: Admit a patient into an alternative ward

          # Implicitly includes 'Attempt to admit a patient into a full ward'
          Background: 
             Suggest alternative ward

          When Mr. Hollness is admitted to bed 2 on Bucklesham ward
          Then the bed becomes occupied
          However he is flagged as off template

patient-history.feature

    Feature: Patient History

       # No need to specify where this background was defined - all background / scenario titles must be unique
       Background: 
          Middleton Hospital

          Scenario: Audit patient

             # Multiple backgrounds
             Background:             
                Admit a patient
                Move patient
                Discharge patient

            When an auditor reviews the patient history
            They can see that he was admitted
            And that he was moved
            And that he was discharged

Still just food for thought

AlbanMinassian commented 10 years ago
       Scenario: Suggest alternative ward
          Background: Attempt to admit a patient into a full ward
          Background: Another unique background
          Given a cardiovascular patient, Mr. Bob Hollness requires admission
          ...

Ami44

cressie176 commented 10 years ago

I'm not keen on duplicating the word Background and the indentation would be optional. However I prefer your syntax for single background includes, e.g.

Scenario: Single Background

   Background: Attempt to admit a patient into a full ward

   Given a cardiovascular patient, Mr. Bob Hollness requires admission

Scenario: Multiple Background

   Background: 
   Attempt to admit a patient into a full ward
   Another unique background

   Given a cardiovascular patient, Mr. Bob Hollness requires admission

Something else which I thought might make the specification more visually appealing is the inclusion of an optional Steps token

Scenario: Multiple Background

   Background: 
      Attempt to admit a patient into a full ward
      Another unique background

   Steps:
      Given a cardiovascular patient, Mr. Bob Hollness requires admission
cressie176 commented 10 years ago

Backgrounds have almost ready. These will be implemented as per Cucumber and https://github.com/acuminous/yadda/pull/60

Re-usable scenarios are somewhat harder. In issue #70 simoami suggested following jBehave's syntax

# A precondition to entire story
GivenFeature: path/to/precondition1.feature

Scenario:  A scenario in which the user can run additional features as pre-requisites

    # preconditions to scenario    
    GivenFeature: path/to/precondition2.feature, 
    Given ... // normal scenario steps

There are two problems with this...

So instead of including a path in the feature, I think we need a way to reference a scenario. I've been thinking of something like...

Scenario: Some reuseable scenario

   Given A
   When B

Scenario: A scenario in which the user can run other scenarios

   [ Some reusable scenario ]
   When I do something
   [ Some reusable scenario ]

The above solution implies that the FeatureParser will have to know about all feature files, without necessarily running them.

Thoughts?

simoami commented 10 years ago

Hi Stephen, Sorry I just saw this.

The syntax to be more natural.

Not sure I understand this point.

path/to/precondition.feature implies Yadda is always run in node, but it can also be run from a browser.

Not sure what limitations you're referring to. The specified path is typically relative so it's possible to resolve it. Otherwise having a base path to the entire test suite, would help as well. Browsers as you know have the ability to load and process files asynchronously.

Also, by [ Some reusable scenario ] do you mean something like:

GivenScenario "some description identifying a given scenario"

I think it only presents half the value to reference external scenarios. And because scenarios live inside feature files it would be hard to identify the feature files that contain them. Our QA team is literally asking for a way to incorporate external tests from other modules as prerequisites. We run a mutli-product SaaS application where modules have dependencies and effects on other modules. So we need to have cross-module checks.

The GivenFeature is relevant and is being measured in this feature comparison: http://mkolisnyk.blogspot.com/2013/03/jbehave-vs-cucumber-jvm-comparison.html

See section "Input Data Sources"

cressie176 commented 10 years ago

Hi Simo,

The syntax to be more natural.

Sorry, I didn't explain myself very well. I would prefer not to include a relative path to another feature file within the specification. Instead I would prefer to refer to an included scenario as a user would do verbally, in a play script or legal document

Not sure what limitations you're referring to. The specified path is typically relative so it's possible to resolve it.

Agreed. I was being thick.

Also, by [ Some reusable scenario ] do you mean something like:

GivenScenario "some description identifying a given scenario"

Not quite, I literally mean

[ some description identifying a given scenario ]

It's quite common to use [ ] brackets in legal documents to refer to other sections. I'm not sure if there's an equivalent syntax in play scripts (e.g. for events which happen off stage or directions to the actors)

I think it only presents half the value to reference external scenarios.

Which half is it missing?

And because scenarios live inside feature files it would be hard to identify the feature files that contain them.

I think you would need to scan the fs or provide a feature file index for browser.

Our QA team is literally asking for a way to incorporate external tests from other modules as prerequisites.

An alternative which might meet your needs and stay consistent with Yadda's philosophy might be to allow the feature parser to respond to annotations, similar to how compiler directives work, e..g

# A precondition to entire story
@Run: path/to/precondition1.feature
Feature: some feature

@Run: path/to/precondition2.feature
Scenario:  A scenario in which the user can run additional features as pre-requisites

    Given ... // normal scenario steps 

You would have to initialise the FeatureParser with the callbacks, e.g.

jsnew FeatureParser({ annotationHandlers: { run: loadPrecondition })

The GivenFeature is relevant and is being measured in this feature comparison

I agree this feature is important, but I don't want to rush it. Getting it wrong will cause a backwards compatibility headache. A temporary alternative might be to write your own FeatureParser. They are easy to plugin to the mocha parser if that's what you're using.

ghost commented 10 years ago

Out of curiosity, are you still working on this? I really really want to use this in my project already. Otherwise I have to change from framework maybe, and that would be a shame because i love it.

What I am looking for

Scenario: Just a scenario with reusable scenario's

Given I opened the app
I first want to run scenario X
And I want to do something I want
Then I want to run scenario Y also
And maybe even another step
cressie176 commented 10 years ago

Unfortunately I'm not getting much time for Yadda at the moment. I'm still fixing bugs and responding to questions. If there's a quick improvement I'll consider it too, but anything significant is on hold.

The real problem is that this and some of the other feature request aren't easy with the current architecture. Rather than twist the code base in knots I'm working on a rewrite, which will hopefully make improvements like this much easier.