Notes On Cucumber and BDD

Cucumber-512I’ve been learning a lot about the theory and history of BDD recently, and where the Cucumber framework sits in all this. I’d thought I’d share my notes, as there are many misconceptions about both these things; as always with my ‘notes’ posts, the format is a little sparse and unstructured, and is aimed at people who already have exposure to these technologies, the idea being that you can scan it easily and pick out ideas…..

  • You have feature files, step code and shared module code (TheWorld -a shared context for each test)
  • Given is a method call to cucumber, run when cucumber starts
    • it registers strings/templates/regexes against the code blocks passed in
  • You can use basic exceptions to do tests, no need for third party libraries
  • Complex regex patterns in cucumber are an anti-pattern -leads to convoluted steps
  • step code should be short, two or three lines, try and keep complexity in the Worlds codebase
  • World(SumHelper) is a method call that registers object as mix-in, then methods of object can be used in tests
  • Where should code go in relation to level of behaviour?
    • Precepts (high level behaviour, eg someone wants to by an item) don’t change much
    • Workflows (Mid level eg a person scans an item into a checkout and then pays) change sometimes
    • Concrete activities (eg person scans an item, selects quantity, places item in bag, selects payment option by clicking button x) change frequently (UI redesign)
    • Feature files are hard to change -you need to change the same thing over and over, plus politics/knock on effects
    • Step code is quite hard to change – you may need to update feature files etc
    • World code isn’t that hard to change -everything is one place
    • Therefore: Concrete activities (the use of GUI) should be defined in the world code, workflows in the step code and precepts in the feature code.
  • BDD is about defining the behaviour, then getting the code to match.
  • Writing tests (TDD) is really about designing code
  • TDD developed into ‘double loop TDD’ where there was an additional, external business feedback cycle driving the TDD cycle. This evolved and was rebranded BDD by Dan North.
  • BDD is often thought to be about the tooling (cucumber etc) but really it’s about having the right conversations.
  • Its all about writing the software that’s needed
  • There are Imperative style scenarios:
    • Given that I click login button
    • And I enter my name
    • And I enter my password
    • Then the page is loaded
    • And the date widget is shown in the corner
  • There are declarative
    • Given that I log in to the homepage
    • I should see the date widget
  • Declarative are easier to maintain, clearer and better for business/BDD
  • Imperative are harder to maintain, brittle and only suit cases where you need to specify low level activities in great detail (in which case, you may want to use a different tool)
  • Strike the right balance between the two extremes, based on your audience and the purpose of the test in question (is it a BDD scenario, an integration test, or a UI test?)
  • Sometimes you can mix the two (eg the first scenario might be a step by step definition of logging in, then all subsequent scenarios would have ‘Given that I am logged in’
  • Are you using problem language or solution language?
  • Have a shared ubiquitous language across business people and developers
  • From a BDD perspective, concrete examples are better than abstract acceptance criteria (eg ‘Then the checkout total should read £1.50’, rather than ‘The checkout total should reflect the sum of all scanned items’
  • Small Cohesive Tests add value, you can immediately show your stakeholders that you are making progress.
  • Bundler is a useful tool that traverses the dependency tree and tries to come up with a non conflicting set of versions
  • Bundler.exec effectively ‘walls off’ all the other versions of gems that aren’t in the gem.lock file, so that when it invokes Ruby, Ruby picks up the correct version of a gem (Ruby doesn’t understand the concept of versions natively, so can’t ‘select’ the right version of a gem
  • Mocking
    • allow(Time).to receive(:now).and_return(Time.parse(‘2018.-2.-18’)
  • GYOEWIN (Give Your Object Exactly What It Needs)
  • Refactoring
    • Chunking steps into single, more declarative steps
    • Background
      • A set of steps that run before each scenario
      • saves duplication
      • Not always easy to bear in mind, especially in large files with many scenarios, or is overriden later in file
    • Tags
      • Can be used to filter which scenarios to run
      • Can also be attached to code hooks (eg. run this code or these steps before this tag, wherever you see it
    • steps keyword
      • step %Q { Given….Then…And….} allows you to reference other steps within the step code.
      • This is useful for refactoring: you can copy and paste the steps you want to chunk into the steps block, your test should still run, and you can gradually start refactoring  so that the steps become on chunk (ideally offloading as much code as possible to helper methods and the world context -remember, you want the code for each step to be two or three lines
  • Capybara
    • Used to test webapps
    • Utilises a browser driver/framework
      • Selenium -similar to real browser, but slow and brittle
      • Phantom.js -faster, implements JS, but not a browser
      • Headless Chrome
      • Mechanise
        • doesn’t act as a browser, instead traverses the html and calls URLS directly
        • Very quick, but doesn’t implement Javascript

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s