Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Starting TDD with Node.js

Akito0107
November 12, 2016

Starting TDD with Node.js

Node学園祭2016でのLT資料です。

Akito0107

November 12, 2016
Tweet

More Decks by Akito0107

Other Decks in Programming

Transcript

  1. ES2015 / ES2016 / ES2017 • Node.js (ͱv8) ͷαϙʔτ͕ͲΜͲΜਐΜͰ ͍Δ

    • Node6͸ES2015 99%αϙʔτ • Node4 (1೥લ)͸57%…. • ͜ͷྲྀΕʹ৐Γ஗Εͨ͘ͳ͍
  2. ΧδϡΞϧʹม׵ • e.g. ES2015෩ʹॻ͖௚͢ function plzRefactorToArrow() { return { value:

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

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

    0, increment: () => { ++this.value; return this.value; } } }
  5. What is TDD ?? • Test Driven Development 
 (ςετۦಈ։ൃ)

    • ։ൃର৅ͷػೳʹରͯ͠ҎԼͷϓϩηεΛ܁Γฦ͢;
 1. ςετΛॻ͖ɺfailͤ͞Δ
 2. ػೳΛ࣮૷͠ɺςετΛpassͤ͞Δ
 3. ϦϑΝΫλ
  6. 0. ؀ڥઃఆ • Testing framework / test runner • mocha

    / ava / eater / etc… • ςετࣗಈԽπʔϧ΋ඞਢ • ϑΝΠϧมߋΛݕ஌ͯࣗ͠ಈͰtestΛճ͢ • npm-watch / mocha -w / etc…
  7. Example: todo list • (expressͷshopping applicationΛ૝ఆ) • API GET /items

    • response bodyʹ ‘items’ property͕͋Δ • items͸DB͔ΒऔಘͰ͖Δʢࠓճ͸mock) • pagination (limit: 30)
  8. ࠷ॳͷ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) }); });
  9. ࠷ॳͷ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) }); });
  10. ࠷ॳͷ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) }); });
  11. ࠷ॳͷ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) }); });
  12. 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) }); });
  13. 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) }); });
  14. 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); }); });
  15. 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); }); });
  16. 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); }); });
  17. 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); }); });
  18. Red

  19. 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); }); .....................
  20. 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); }); .....................
  21. 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); }); .....................
  22. 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); });
  23. RED

  24. 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);
  25. 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΁ͷॻ͖ग़͠
  26. 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΁ ֎޲͖ͷڍಈ͕͔Θ͍ͬͯͳ͍͜ͱΛ֬ೝ
  27. 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)) } }; }