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

Starting TDD with Node.js

Avatar for Akito0107 Akito0107
November 12, 2016

Starting TDD with Node.js

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

Avatar for Akito0107

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)) } }; }