Slide 1

Slide 1 text

Node.jsͰ࢝ΊΔ
 TDD Akito Ito

Slide 2

Slide 2 text

ࣗݾ঺հ • ҏ౻ɹӯɹ(͍ͱ͏ ͖͋ͱ) • ϦΫϧʔτςΫϊϩδʔζͱ͍͏ձࣾͰ
 Node.js੡γεςϜͷӡ༻։ൃΛ͍ͯ͠·͢ • @Akito0107

Slide 3

Slide 3 text

ES2015 / ES2016 / ES2017 • Node.js (ͱv8) ͷαϙʔτ͕ͲΜͲΜਐΜͰ ͍Δ • Node6͸ES2015 99%αϙʔτ • Node4 (1೥લ)͸57%…. • ͜ͷྲྀΕʹ৐Γ஗Εͨ͘ͳ͍

Slide 4

Slide 4 text

ΧδϡΞϧʹม׵ • e.g. ES2015෩ʹॻ͖௚͢ function plzRefactorToArrow() { return { value: 0, increment: function() { ++this.value; return this.value; } } }

Slide 5

Slide 5 text

ΧδϡΞϧʹม׵ • e.g. ES2015෩ʹॻ͖௚͢ function arrow() { return { value: 0, increment: () => { ++this.value; return this.value; } } }

Slide 6

Slide 6 text

ΧδϡΞϧʹม׵ • e.g. ES2015෩ʹॻ͖௚͢ function arrow() { return { value: 0, increment: () => { ++this.value; return this.value; } } }

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

ݱ࣮͸ݫ͍͠ • ՔಇதͷαʔϏεΛES2015 / 2016ʹఴͬͯ
 ॻ͖௚͢ͷ͸؆୯Ͱ͸ͳ͍ɻ • ݴ͍Α͏ͷͳ͍ෆ҆ͱͷઓ͍ • ͱ͸͍͑ɺ৽͍͠ػೳ΋࢖͍͖͍ͬͯͨ

Slide 9

Slide 9 text

ݱ࣮͸ݫ͍͠ • ՔಇதͷαʔϏεΛES2015 / 2016ʹఴͬͯ
 ॻ͖௚͢ͷ͸؆୯Ͱ͸ͳ͍ɻ • ݴ͍Α͏ͷͳ͍ෆ҆ͱͷઓ͍ • ͱ͸͍͑ɺ৽͍͠ػೳ΋࢖͍͖͍ͬͯͨ TDD

Slide 10

Slide 10 text

What is TDD ?? • Test Driven Development 
 (ςετۦಈ։ൃ) • ։ൃର৅ͷػೳʹରͯ͠ҎԼͷϓϩηεΛ܁Γฦ͢;
 1. ςετΛॻ͖ɺfailͤ͞Δ
 2. ػೳΛ࣮૷͠ɺςετΛpassͤ͞Δ
 3. ϦϑΝΫλ

Slide 11

Slide 11 text

TDD Cycle UPEPMJTUͷ࡞੒ ςετ࡞੒ 3FE ؀ڥઃఆɹ ػೳ࣮૷ (SFFO ϦϑΝΫλ

Slide 12

Slide 12 text

0. ؀ڥઃఆ • Testing framework / test runner • mocha / ava / eater / etc… • ςετࣗಈԽπʔϧ΋ඞਢ • ϑΝΠϧมߋΛݕ஌ͯࣗ͠ಈͰtestΛճ͢ • npm-watch / mocha -w / etc…

Slide 13

Slide 13 text

1. TodoϦετͷ࡞੒ • ࠓ͔Β࣮૷͢ΔػೳϦετ • ࢓༷ͳͲ͕൑໌͍ͯ͠Δ৔߹͸ͦΕ΋
 ͔ͬ͠Γॻ͘ • ࣮૷ͷॱ൪Λ༏ઌॱҐ΍TestabilityʹΑͬͯ
 ܾఆ͢Δ • ஍ຯʹେࣄͳͷͰαϘΒͣʹॻ͜͏

Slide 14

Slide 14 text

Example: todo list • (expressͷshopping applicationΛ૝ఆ) • API GET /items • response bodyʹ ‘items’ property͕͋Δ • items͸DB͔ΒऔಘͰ͖Δʢࠓճ͸mock) • pagination (limit: 30)

Slide 15

Slide 15 text

2. Write a test • Todo list͔Β1ͭػೳΛpopͯ͠ɺ࠷ॳʹςε τΛॻ͘ • ඞͣςετ͕མͪΔ͜ͱΛ֬ೝ͢Δʂ

Slide 16

Slide 16 text

࠷ॳͷExpressϋϯυϥ var express = require('express'); var request = require('supertest'); var assert = require('power-assert'); function index(req, res) { res.send(); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 17

Slide 17 text

࠷ॳͷExpressϋϯυϥ var express = require('express'); var request = require('supertest'); var assert = require('power-assert'); function index(req, res) { res.send(); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 18

Slide 18 text

࠷ॳͷExpressϋϯυϥ var express = require('express'); var request = require('supertest'); var assert = require('power-assert'); function index(req, res) { res.send(); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 19

Slide 19 text

࠷ॳͷExpressϋϯυϥ var express = require('express'); var request = require('supertest'); var assert = require('power-assert'); function index(req, res) { res.send(); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 20

Slide 20 text

Fail (RED)

Slide 21

Slide 21 text

3. Production Codeͷ࣮૷ • Test͕Pass͢Δ࠷খݶͷίʔυΛॻ͘ • (͜ͷஈ֊Ͱ͸)ՄಡੑͳͲ͸ߟྀ͠ͳ͍

Slide 22

Slide 22 text

3. Production Codeͷ࣮૷ function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 23

Slide 23 text

3. Production Codeͷ࣮૷ function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { it('response body contains items', function(done) { var app = express(); app.get('/items', index); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); });

Slide 24

Slide 24 text

Pass (Green)

Slide 25

Slide 25 text

TDD Cycle UPEPMJTUͷ࡞੒ ςετ࡞੒ 3FE ؀ڥઃఆɹ ػೳ࣮૷ (SFFO ϦϑΝΫλ 2ͱ3Λ܁Γฦ͢

Slide 26

Slide 26 text

Write a Test function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { . . . . . . . . . it('Get Items from DB', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }]; var app = express(); app.get('/items', index); request(app) .get('/items') .expect({items: items}, done); }); });

Slide 27

Slide 27 text

Write a Test function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { . . . . . . . . . it('Get Items from DB', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }]; var app = express(); app.get('/items', index); request(app) .get('/items') .expect({items: items}, done); }); });

Slide 28

Slide 28 text

Write a Test function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { . . . . . . . . . it('Get Items from DB', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }]; var app = express(); app.get('/items', index); request(app) .get('/items') .expect({items: items}, done); }); });

Slide 29

Slide 29 text

Write a Test function index(req, res) { res.send({ items: [] }); } describe('GET /items', function() { . . . . . . . . . it('Get Items from DB', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }]; var app = express(); app.get('/items', index); request(app) .get('/items') .expect({items: items}, done); }); });

Slide 30

Slide 30 text

Red

Slide 31

Slide 31 text

Implement function index(options) { var Item = options.Item return function(req, res) { Item.find({}, function(err, docs) { res.send({ items: docs }); }); } } ..................... it('Get items', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }] var Item = { find: function(opts, cb) { setImmediate(function() { cb(null, items); }); } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect({ items: items }, done); }); .....................

Slide 32

Slide 32 text

Implement function index(options) { var Item = options.Item return function(req, res) { Item.find({}, function(err, docs) { res.send({ items: docs }); }); } } ..................... it('Get items', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }] var Item = { find: function(opts, cb) { setImmediate(function() { cb(null, items); }); } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect({ items: items }, done); }); .....................

Slide 33

Slide 33 text

Implement function index(options) { var Item = options.Item return function(req, res) { Item.find({}, function(err, docs) { res.send({ items: docs }); }); } } ..................... it('Get items', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }] var Item = { find: function(opts, cb) { setImmediate(function() { cb(null, items); }); } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect({ items: items }, done); }); .....................

Slide 34

Slide 34 text

Green

Slide 35

Slide 35 text

Write a Test it('Pagenation with limit=30', function(done) { var items = _.range(0, 31).map(function(i) { return { id: i.toString(), name: 'name', price: 1200 } }); var Item = { find: function(opts, cb) { setImmediate(function() { cb(null, items); }); }, }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect(function(response) { assert.equal(response.body.items.length, 30) }) .end(done); });

Slide 36

Slide 36 text

RED

Slide 37

Slide 37 text

Implement function index(options) { var Item = options.Item return function(req, res) { Item.paginate({}, {}, function(err, docs) { res.send({ items: docs }); }); } } it('Pagenation with limit=30', function(done) { var items = _.range(0, 31).map(function(i) { return { id: i.toString(), name: 'name', price: 1200 } }); var Item = { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 30 * page)); }) } }; .............. request(app) .get('/items') .expect(function(response) { assert.equal(response.body.items.length, 30) }) .end(done);

Slide 38

Slide 38 text

Green

Slide 39

Slide 39 text

TDD Cycle UPEPMJTUͷ࡞੒ ςετ࡞੒ 3FE ؀ڥઃఆɹ ػೳ࣮૷ (SFFO ϦϑΝΫλ ͩΜͩΜίʔυ͕ԚΕͯ͘Δ ଱͖͑Εͳ͘ͳͬͨΒ
 ϦϑΝΫλ

Slide 40

Slide 40 text

4. Refactor • ʮ֎޲͖ͷڍಈΛม͑ͣʹίʔυͷ಺෦ߏ଄ Λม͑Δ͜ͱʯ • TDDͷจ຺Ͱ͸ɺʮ֎޲͖ͷڍಈΛม͑ͳ ͍͜ͱʯ͸୯ମςετ͕อূ͢Δ • ϦϑΝΫλϦϯάͷ࠷த͸ৗʹςετ͕ GreenͰ͋Δ͜ͱΛ֬ೝ

Slide 41

Slide 41 text

Refactor 1: 
 ॏෳͷഉআ it('response body contains items', function(done) { var items = [] var Item = { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 30 * page)); }) } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect(function(response) { assert(response.body.items); }) .end(done) }); it('Get items', function(done) { var items = [{ id: '12345', name: 'Javascript: The Good Parts', price: 1800 }] var Item = { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 30 * page)); }) } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect({ items: items }, done); }); it('Pagenation with limit=30', function(done) { var items = _.range(0, 31).map(function(i) { return { id: i.toString(), name: 'name', price: 1200 } }); var Item = { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 30 * page)); }) } }; var app = express(); app.get('/items', index({ Item: Item })); request(app) .get('/items') .expect(function(response) { assert.equal(response.body.items.length, 30) }) .end(done); }); }); function initMockDb(items) { return { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 
 30 * page)); }) } }; } helper / util΁ͷॻ͖ग़͠

Slide 42

Slide 42 text

Refactor 2: cb => Promise function index(options) { var Item = options.Item return function(req, res) { Item.paginate({}, {}, function(err, docs) { res.send({ items: docs }); }); } } function initMockDb(items) { return { paginate: function(query, opts, cb) { setImmediate(function() { var page = opts.page || 1 cb(null, items.slice(30 * (page - 1), 30 * page)); }) } }; } function index(options) { var Item = options.Item return function(req, res) { Item.paginate({}, {}) .then(function(docs) { res.send({ items: docs }); }); } } function initMockDb(items) { return { paginate: function(query, opts) { var page = opts.page || 1 var resp = items.slice(30 * (page - 1), return Promise.resolve(resp) } }; } callback styleͷAPIΛPromise΁ ֎޲͖ͷڍಈ͕͔Θ͍ͬͯͳ͍͜ͱΛ֬ೝ

Slide 43

Slide 43 text

Refactor 3: ES2015 / 2016 function index(options) { var Item = options.Item return function(req, res) { Item.paginate({}, {}) .then(function(docs) { res.send({ items: docs }); }); } } function initMockDb(items) { return { paginate: function(query, opts) { var page = opts.page || 1 var resp = items.slice(30 * (page - 1), return Promise.resolve(resp) } }; } function index(options = {}) { const Item = options.Item return (req, res) => { Item.paginate({}, {}).then((docs) => { res.send({ items: docs }); }); } } function initMockDb(items) { return { paginate: (query, opts) => { const page = opts.page || 1 return Promise.resolve(
 items.slice(30 * (page - 1), 30 * page)) } }; }

Slide 44

Slide 44 text

Refactor: ߟ࡯ • ”֎޲͖ͷڍಈΛม͑ͣʹίʔυͷ಺෦ߏ଄Λม ͑Δ͜ͱ” • ৽จ๏Λ࢖͍ͬͯͯ΋ɺڍಈ͕͔Θ͍ͬͯͳ ͍͜ͱ͸TDDͷϓϩηεͰ࡞੒ͨ͠୯ମςε τ͕อূͯ͘͠ΕΔ • ҆৺ͯ͠ίʔυΛ৽͍͠จ๏ʹ௚͍͚ͯ͠Δ

Slide 45

Slide 45 text

Conclusion • ςετ㱻࣮૷Λ܁Γฦ͠ɺզຫͰ͖ͳ͘ͳͬ ͨΒϦϑΝΫλ • TDD͸ܧଓతϦϑΝΫλͷ؀ڥΛ࡞Δͱ͍͏ ఺Ͱ΋ඇৗʹ༗ޮ • ݴޠͷਐԽ͕ૣ͍Javascriptͦ͜TDDΛ
 ಋೖ͍ͯ͜͠͏ʂ

Slide 46

Slide 46 text

Thank you for listening!