name: inverse layout: true class: center, middle, inverse --- # TARTARE ### Making BDD testing with Javascript --- class: center, middle ## BDD testing concepts --- layout: false ## Acceptance Tests Scenarios that guarantee that customers requirements
have been met and the system is acceptable ## Test automation Use of special software to - control the execution of tests - compare actual outcomes with expected outcomes ## Tools vs Code --- ## BDD (Behaviour Driven Development) Tests of any unit of software should be specified in terms of the desired behavior of the unit How: - Business analysts, developers and testers should collaborate and specify the behavior in terms of user stories - Using a semi-formal language shared by all members of the team (both technical and non-technical) --- ## BDD (Behaviour Driven Development) - Title - Narrative: - Who is the driver or primary stakeholder of the story - Which effect the stakeholder wants the story to have - What business value the stakeholder will derive from this effect - Acceptance criteria or Scenarios: - Initial condition - Events triggering the start of the scenario - Expected outcome --- ## Gherkin Business Readable, Domain Specific Language that lets you describe software’s behaviour without detailing how that behaviour is implemented - Define automated tests - Document the system behaviour --
``` Feature: Some terse yet descriptive text of what is desired Textual description of the business value of this feature Business rules that govern the scope of the feature Any additional information that will make the feature easier to understand Scenario: Some determinable business situation Given some precondition And some other precondition When some action by the actor And some other action And yet another action Then some testable outcome is achieved And something else we can check happens too ``` --- ## Data Driven Testing - Usage of a table with test inputs and verifiable outputs - The tester supplies the inputs from a row in the table and expects the outputs which occur in the same row - The table typically contains values which correspond to boundary or partition input spaces - It is easy to add additional inputs to the table when new partitions are discovered or added to the product --
``` Scenario Outline: eating Given there are <start> cucumbers When I eat <eat> cucumbers Then I should have <left> cucumbers Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 | ``` --- template: inverse ## Tartare Framework --- ## What & Why Tartare is a BDD Gherkin framework and a QA tools library By the time Tartare was born, existent BDD frameworks for Javascript/Node.js didn't fit our needs: - Immature frameworks lacked key features (example tables) - Lack of proper hooks - Not using Mocha behind the scenes (unit testing is being done with Mocha) - Mocha itself use BDD but not Gherkin syntax - Asynchronous code may mess up steps implementation (callback hell) --- ## Getting started Installing tartare: ```bash $ npm install tartare ``` Using tartare: ```bash $ tartare test.js ``` Generating a markdown report: ```bash $ tartare test.js --reporter gherkin-md --bugid-link "https://github.com..." ``` --- ## Exercise Scenario
``` Feature: Get users using the API I am able to use the Users API to read the users of the SUT Scenario: Get a user that exists in the SUT Given there is a user with username john in the SUT When I use the API to get the user looking up by his name Then I get a 200 OK HTTP Status Code And the HTTP body has the expected user data ``` --- ## New keywords
```javascript feature('Get users using the API', 'I am able to use the Users API to read the users of the SUT', function() { scenario('Get a user that exists in the SUT', function() { given('there is a user with username john in the SUT', function() { }); when('I use the API to get the user looking up by his name', function() { }); then('I get a 200 OK HTTP Status Code', function() { }); and('the HTTP body has the expected user data', function() { }); }); }); ``` --- ## Synchronizing code ```javascript var tartare = require('tartare'); var steps = require('./steps'); [...] when('I use the API to get the user looking up by his name', function(done) { steps.getUser('john', function(err, res) { if (err) { return done(err); } response = res; done(); }); }); [...] ``` ```javascript var tartare = require('tartare'); var steps = require('./steps'); tartare.synchronize(steps); [...] when('I use the API to get the user looking up by his name', function(done) { response = steps.getUser('john'); }); [...] ``` --- ## DDT: Variants
```javascript feature('Get users using the API', function() { var dataset = [ { desc: 'John', user: { name: 'john', email: 'john@doe.com' } }, { desc: 'Alice', user: { name: 'alice', email: 'alice@doe.com' } } ]; scenario('Get a user that exists in the SUT', dataset, function(variant) { [...] when('I use the API to get the user looking up by his name', function() { response = steps.getUser(variant.user.name); }); [...] }); }); ``` --- ## Hooks .left-column[ - beforeAll - beforeFeature - beforeEachScenario - beforeScenario - beforeEachVariant ] .right-column[ - afterAll - afterFeature - afterEachScenario - afterScenario - afterEachVariant ] .clear-column[ ```javascript feature('Get users using the API', function() { beforeFeature(function() { steps.startServer(); }); scenario('Get a user that exists in the SUT', function() { [...] }); afterFeature(function() { steps.stopServer(); }); }); ``` ] --- ## Manual tests
```javascript feature('Switch on and off the device', function() { scenario('Switch on the device', function () { given('the device is switched off'); when('I press the switch on button'); then('the device is up & running'); }); }); ``` --- ## Only and Skip
```javascript feature('Get users using the API', function() { var dataset = [ { desc: 'John', user: { name: 'john', email: 'john@doe.com' } }, * { skip: true, desc: 'Alice', user: { name: 'alice', email: 'alice@doe.com' } } ]; * scenario.only('Get a user that exists in the SUT', dataset, function(variant) { [...] }); }); feature('Switch on and off the device', function() { scenario('Switch on the device', function() { [...] }); }); ``` --- ## Tags and Filters
```javascript feature('Get users using the API', function() { var dataset = [ * { tag: 'critical', desc: 'John', user: { name: 'john', email: 'john@doe.com' } }, { desc: 'Alice', user: { name: 'alice', email: 'alice@doe.com' } } ]; scenario('Get a user that exists in the SUT', dataset, function(variant) { [...] }); }); feature('Switch on and off the device', function() { scenario('Switch on the device', function() { [...] * }).tag('critical'); }); ``` ```bash $ tartare test.js --filter +critical ``` --- ## Bug Management
```javascript feature('Get users using the API', function() { var dataset = [ { desc: 'John', user: { name: 'john', email: 'john@doe.com' } }, { desc: 'Alice', user: { name: 'alice', email: 'alice@doe.com' } } ]; scenario('Get a user that exists in the SUT', dataset, function(variant) { [...] * }).minorBug('1'); }); ``` --- ## Tartare as a QA Library - Programmable API Server Mock - Customized Chai Plugins for HTTP and UNICA APIs - An HTTP REST client to make easier testing REST APIs - SUT start/stop: A set of functions to manage servers configuration, start and stop --- ## Where is Tartare being used
.large[ - TDAF reusable components - eyeOS ]
.left-column[
] .righ-column[
] --- template: inverse # Q & A [https://github.com/JoseAntonioRodriguez/tartare-talk](https://github.com/JoseAntonioRodriguez/tartare-talk) .footnote[Slideshow created using [remark](http://github.com/gnab/remark).]