---
### 1st attempt
```javascript
let milkAvailable = false;
function milkCow() {
console.log("Starting to milk cow...");
setTimeout(function() {
console.log("Milk is available.");
milkAvailable = true;
}, 2000);
}
milkCow();
console.log("Can I drink my milk? (" + milkAvailable + ")");
```
---
### Solution
```javascript
let milkAvailable = false;
*function milkCow(done) {
console.log("Starting to milk cow...");
setTimeout(function() {
console.log("Milk is available.");
milkAvailable = true;
* done()
}, 2000);
}
milkCow(function () {
console.log("Can I drink my milk? (" + milkAvailable + ")");
});
```
---
### Sequence
Ok... but what happens when I have more than 2 tasks that I want to execute in sequence?
```javascript
function display(value) {
console.log("display " + value);
}
function displaySoon(value) {
setTimeout(function timer() {
console.log("display soon " + value);
}, 2000);
}
display(1);
display(2);
displaySoon(3);
displaySoon(4);
displaySoon(5);
display(6);
// outputs: 1, 2, 6, 3, 4, 5
```
[Analyze it with loupe](http://latentflip.com/loupe/?code=ZnVuY3Rpb24gZGlzcGxheSh2YWx1ZSkgewogIGNvbnNvbGUubG9nKCJkaXNwbGF5ICIgKyB2YWx1ZSk7Cn0KCmZ1bmN0aW9uIGRpc3BsYXlTb29uKHZhbHVlKSB7CiAgc2V0VGltZW91dChmdW5jdGlvbiB0aW1lcigpIHsKICAgIGNvbnNvbGUubG9nKCJkaXNwbGF5IHNvb24gIiArIHZhbHVlKTsKICB9LCAyMDAwKTsKfQoKZnVuY3Rpb24gc3RhcnQoKSB7CiAgICBkaXNwbGF5KDEpOwogICAgZGlzcGxheSgyKTsKICAgIGRpc3BsYXlTb29uKDMpOwogICAgZGlzcGxheVNvb24oNCk7CiAgICBkaXNwbGF5U29vbig1KTsKICAgIGRpc3BsYXkoNik7CiAgICBkaXNwbGF5KDcpOwogICAgZGlzcGxheSg4KTsKfQoKc3RhcnQoKTs%3D!!!)
---
### Sequence
```javascript
function display(value) {/* ... */}
*function displaySoon(value, callback) {
setTimeout(function timer() {
console.log("display soon " + value);
* callback()
}, 2000);
}
display(1);
display(2);
displaySoon(3, function() {
displaySoon(4, function() {
displaySoon(5, function() {
display(6);
});
});
});
```
[Analyze it with loupe](http://latentflip.com/loupe/?code=ZnVuY3Rpb24gZGlzcGxheSh2YWx1ZSkgewogIGNvbnNvbGUubG9nKCJkaXNwbGF5ICIgKyB2YWx1ZSk7Cn0KCmZ1bmN0aW9uIGRpc3BsYXlTb29uKHZhbHVlLCBjYWxsYmFjaykgewogIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICBjb25zb2xlLmxvZygiZGlzcGxheSBzb29uICIgKyB2YWx1ZSk7CiAgICAgY2FsbGJhY2soKQogIH0sIDIwMDApOwp9CgpmdW5jdGlvbiBzdGFydCgpIHsKICAgIGRpc3BsYXkoMSk7CiAgICBkaXNwbGF5KDIpOwogICAgZGlzcGxheVNvb24oMywgZnVuY3Rpb24oKSB7CiAgICAgIGRpc3BsYXlTb29uKDQsIGZ1bmN0aW9uKCkgewogICAgICAgIGRpc3BsYXlTb29uKDUsIGZ1bmN0aW9uKCkgewogICAgICAgICAgZGlzcGxheSg2KTsKICAgICAgICB9KTsKICAgICAgfSk7CiAgICB9KTsKfQoKc3RhcnQoKTsKY29uc29sZS5sb2coJ2ZpbmlzaGVkID8nKTs%3D!!!)
---
### Parallel
Now, let’s imagine that we have 3 asynchronous tasks. We want to invoke them in parallel and wait until all of them complete.
Typical use case: you want to send several AJAX requests (to get different data models) and update your DOM once you have received all responses.
```javascript
function fetchAll(done) {
fetchData('/api/users/user1');
fetchData('/api/users/user2');
fetchData('/api/users/user3');
done();
}
```
--
**Double fail: not only do I invoke `done()` too early, but also I don’t have any result to send back...**
---
### Parallel
```javascript
function fetchAll(done) {
const results = [];
let numberOfPendingTasks = 3;
function reportResults(result) {
results.push(result);
numberOfPendingTasks -= 1;
if (numberOfPendingTasks === 0) {
done(results);
}
}
fetchData('/api/users/user1', reportResults);
fetchData('/api/users/user2', reportResults);
fetchData('/api/users/user3', reportResults);
}
```
[](https://codesandbox.io/s/p2pw7pw73m)
---
### Webcasts
- [Async with callbacks (1) : overview](https://youtu.be/EFTSOLhnlbE)
- [Async with callbacks (2): create a sync version first](https://youtu.be/WKE3by3k2lY)
- [Async with callbacks (3): refactor the class and introduce a private function](https://youtu.be/jLdboiiD5u0)
- [Async with callbacks (4): refactor the class: async signature and modification of the test suite](https://youtu.be/pU-HTLQ63AA)
- [Async with callbacks (5): fixing the problem and calling done() when everything is done](https://youtu.be/lcqqgnyPWxc)
- [Async with callbacks (6): write a function to fetch all pages](https://youtu.be/6b_5bdIj5Bo)
---
### References
- [Philip Roberts: What the heck is the event loop anyway? | JSConf EU](https://youtu.be/8aGhZQkoFbQ)
---
layout: false
class: center, middle
name: automated-testing

## Automated testing
---
layout: true

.breadcrumbs[[Automated testing](#automated-testing)]
---
### Why should I write automated tests?
.bigger[
- Automated testing is important for **quality** and continuous delivery
- Writing tests is also an approach to **design** and **document** software (TDD)
]
???
- TDD: Use the program even before it exists
---
### Solution
.bigger[
- Select a testing framework: [mocha.js](https://mochajs.org/)
- Select an assertion library: [chai.js](https://www.chaijs.com/)
- Write tests to get familiar Javascript
]
---
### Install Mocha
First you need a test runner. Mocha is a popular testing framework that runs your tests serially and show results in your terminal.
Create a javascript project, with a `test` folder and a `sample-test.js` file :
```sh
my-project
├── test
│ └──sample-test.js
└── package.json
```
Then run the following command to install mocha locally as a development dependency :
```sh
$ npm install --save-dev mocha
```
---
### Run a simple test
Write a simple test suite, then run `./node_modules/.bin/mocha`
```javascript
// test/sample-test.js
const assert = require('assert');
describe('String', function () {
it('should replace some characters', function () {
const name = 'paulnta'
.replace('au', 'o')
.replace('n', 'en');
assert.equal(name, 'polenta');
});
it('will fail', function () {
assert.equal('1' + '1', '2');
// AssertionError [ERR_ASSERTION]: '11' == '2'
});
});
```
By default mocha will execute any `.js` files inside `test` folder and report results in your terminal.
---
### Install Chai
In the previous example we're using Node.js' built-in [assert](https://nodejs.org/api/assert.html) module. - But Mocha allows you to use any assertion library you wish.
In practice we often add an another assertion library such as [Chai](https://www.chaijs.com/) to get more powerful features :
```javascript
expect(name).to.be.a('string');
expect(polenta).to.have.a.property('color')
.with.lengthOf(6);
```
Install chai via npm as follows :
```sh
$ npm install --save-dev chai
```
---
### Install Chai
The previous example could be re-written as follows using `expect` from `chai`
```diff
+const { expect } = require('chai');
describe('String', function () {
it('should transform name', function () {
const name = 'paulnta'.replace('au', 'o').replace('n', 'en');
+ expect(name).to.equal('polenta');
});
it('will fail', function () {
+ expect('1' + '1').to.equal('2');
});
});
```
Chai allows you to use different [assertion styles](https://www.chaijs.com/api/) and [plugins](https://www.chaijs.com/plugins/). Use what makes the most sense for your project.
---
### Running tests with npm
To make your tests easily runnable, add a `test` command to the `script` field of your `package.json`
```javascript
// package.json
"scripts": {
"test": "mocha test/**/*.js"
},
```
Now you can just type the following command to run tests with the configuration you specified.
```sh
$ npm test
```
---
### Webcasts
- [TDD with mocha and chai (1): overview](https://youtu.be/Pngkmih-3SE)
- [TDD with mocha and chai (2): install npm modules](https://youtu.be/xAIrgcN3l0g)
- [TDD with mocha and chai (3): implement test module](https://youtu.be/_9XZV35SEG0)
- [TDD with mocha and chai (4): implement module](https://youtu.be/DGbdKiY85Yw)
- [TDD with mocha and chai (5): refactor to es6 and validate](https://youtu.be/Dlw3Y6HodqA)
- [TDD with mocha and chai (6): one more thing...](https://www.youtube.com/watch?v=YO877449Y0c&list=PLfKkysTy70QZUPYjLkkYcwqvVph4q9cKZ&index=14)