ãã¹ãã«ã¯ã次ã®ãã®ã䜿çšããŸãã
-Mocha-ãã¹ããèšè¿°ããç°¡åãã€ç°¡åã«å®è¡ã§ãããã¬ãŒã ã¯ãŒã¯ã ããŸããŸãªããŒãžã§ã³ã®ã¬ããŒããçæãããã¹ãããããã¥ã¡ã³ããäœæããæ¹æ³ãç¥ã£ãŠããŸãã
-Should- ãæ¿èªãã®ã¹ã¿ã€ã«ã®ãã¹ãçšã©ã€ãã©ãªïŒæ£ããååãèŠã€ãããŸããã§ããïŒ
-SuperTest - nodejs㧠HTTPãµãŒããŒããã¹ãããããã®ã©ã€ãã©ãª
-jscoverage-ãã¹ãã§ã³ãŒãã«ãã¬ããžãè©äŸ¡ãã
ãã¹ãŠããŒãããäœæããããã§ã¯ãããŸããããåã®èšäºã®ã¢ããªã±ãŒã·ã§ã³ããã¹ãã§ã©ããããapp2ãã©ã«ããŒã«å®å šã«ã³ããŒããããšã«ããŸããã
ãŸããå¿ èŠãªã¢ãžã¥ãŒã«ãpackage.jsonãã¡ã€ã«ã«è¿œå ããŸãã å¿ èŠãªã®ã¯ãmochaãshouldãsupertestã§ãã
package.json
{ "name": "app2", "version": "0.0.0", "author": "Evgeny Reznichenko <kusakyky@gmaill.com>", "dependencies": { "express": "3", "jade": "*", "should": "*", "mocha": "*", "supertest": "*" } }
npm iã³ãã³ããå®è¡ããŠãå¿ èŠãªãã¹ãŠã®ã¢ãžã¥ãŒã«ãã€ã³ã¹ããŒã«ããŸãã
jscoverageãã€ã³ã¹ããŒã«ããŸãïŒUbuntu sudo apt-get install jscoverageã®äž ïŒã
次ã«ããããžã§ã¯ãã®ã«ãŒãã«libãã£ã¬ã¯ããªãäœæããããã«app.jsãã³ããŒããŸããããã¯ããã¹ãŠã®ã¹ã¯ãªãããã«ãã¬ããžãã¹ãã§ç°¡åã«ã«ããŒããããã«å¿ èŠã§ãã
app.jsãã¡ã€ã«ãç·šéããŠãµãŒããŒãå€éšã«ãšã¯ã¹ããŒããããããžã§ã¯ãã®ã«ãŒãã«index.jsãã¡ã€ã«ãäœæããŸããããã«ããããµãŒããŒãæ¥ç¶ããããœã±ããã«ãã³ã°ã¢ããããŸãã ãŸãããã¥ãŒãšãããªãã¯ãã£ã¬ã¯ããªãžã®ãã¹ãä¿®æ£ããããšãå¿ããªãã§ãã ããã
次ã®ããã«ãªããŸãã
index.js
var app = require('./lib/app.js'); app.listen(3000);
lib / app.js
var express = require('express'), jade = require('jade'), fs = require('fs'), app = express(), viewOptions = { compileDebug: false, self: true }; //data var db = { users: [ { id: 0, name: 'Jo', age: 20, sex: 'm' }, { id: 1, name: 'Bo', age: 19, sex: 'm' }, { id: 2, name: 'Le', age: 18, sex: 'w' }, { id: 10, name: 'NotFound', age: 18, sex: 'w' } ], titles: { '/users': ' ', '/users/profile': ' ' } }; //utils function merge(a, b) { var key; if (a && b) { for (key in b) { a[key] = b[key]; } } return a; } //App settings app.set('views', __dirname + '/../views'); app.set('view engine', 'jade'); app.set('title', ' '); app.locals.compileDebug = viewOptions.compileDebug; app.locals.self = viewOptions.self; app.use(express.static(__dirname + '/../public')); app.use(app.router); app.use(function (req, res, next) { next('not found'); }); //error app.use(function (err, req, res, next) { if (/not found/i.test(err)) { res.locals.title = ' :('; res.render('/errors/notfound'); } else { res.locals.title = ''; res.render('/errors/error'); } }); app.use(express.errorHandler()); //routes // app.all('*', function replaceRender(req, res, next) { var render = res.render, view = req.path.length > 1 ? req.path.substr(1).split('/'): []; res.render = function(v, o) { var data, title = res.locals.title; res.render = render; res.locals.title = app.get('title') + (title ? ' - ' + title: ''); // // if ('string' === typeof v) { if (/^\/.+/.test(v)) { view = v.substr(1).split('/'); } else { view = view.concat(v.split('/')); } data = o; } else { data = v; } // res.locals // (res.locals.title) data = merge(data || {}, res.locals); if (req.xhr) { // json res.json({ data: data, view: view.join('.') }); } else { // , // ( history api) data.state = JSON.stringify({ data: data, view: view.join('.') }); // . . view[view.length - 1] = '_' + view[view.length - 1]; // res.render(view.join('/'), data); } }; next(); }); // app.all('*', function loadPageTitle(req, res, next) { res.locals.title = db.titles[req.path]; next(); }); app.get('/', function(req, res){ res.render('index'); }); app.get('/users', function(req, res){ var data = { users: db.users }; res.render('index', data); }); app.get('/users/profile', function(req, res, next){ var user = db.users[req.query.id], data = { user: user }; if (user) { res.render(data); } else { next('Not found'); } }); // function loadTemplate(viewpath) { var fpath = app.get('views') + viewpath, str = fs.readFileSync(fpath, 'utf8'); viewOptions.filename = fpath; viewOptions.client = true; return jade.compile(str, viewOptions).toString(); } app.get('/templates', function(req, res) { var str = 'var views = { ' + '"index": (function(){ return ' + loadTemplate('/index.jade') + ' }()),' + '"users.index": (function(){ return ' + loadTemplate('/users/index.jade') + ' }()),' + '"users.profile": (function(){ return ' + loadTemplate('/users/profile.jade') + ' }()),' + '"errors.error": (function(){ return ' + loadTemplate('/errors/error.jade') + ' }()),' + '"errors.notfound": (function(){ return ' + loadTemplate('/errors/notfound.jade') + ' }())' + '};' res.set({ 'Content-type': 'text/javascript' }).send(str); }); module.exports = app;
app.jsãã¡ã€ã«ãèŠãŠã¿ãŸãããã3ã€ã®è«çéšåã«åãããŠããã®ã¯æããã§ãã
-æåã®éšåã§ã¯ãããŒã¿ã¢ãã«ãæäœããããžãã¯å šäœãåãäžããŸãã
-2çªç®ã®éšåããã¹ãŠã®è£å©ãŠãŒãã£ãªãã£ã§ã¯ã ããŒãžæ©èœã®ã¿ããããŸãã
-3çªç®ã¯ãµãŒããŒèªäœã«ãªããŸã
ç§ãã¡ã¯åžæã決ããã®ã§ããã¹ãã®äœæãå§ããŸãããããã¹ããå®è¡ããã®ã«äŸ¿å©ãªMakefileãæåã«äœæãããããžã§ã¯ãã®ã«ãŒãã«é 眮ããŸãã
ã¡ã€ã¯ãã¡ã€ã«
MOCHA = ./node_modules/.bin/mocha test: @NODE_ENV=test $(MOCHA) \ -r should \ -R spec .PHONY: test
--r-Mochaãshouldã©ã€ãã©ãªãæ¥ç¶ããããšã瀺ããŸã
--R-ãã¹ãã¬ããŒãã衚瀺ãã圢åŒã瀺ããŸãã ã¬ããŒãã«ã¯ããã€ãã®ã¿ã€ãããããããã¯æ¬¡ã®ããã«ãªããŸãã
åç
ãããŒã«ãã®ãã¹ã
ããã©ã«ãã§ã¯ãMochaã¯ãã¹ããã£ã¬ã¯ããªãããã¹ããå®è¡ããããããã®ãããªãã£ã¬ã¯ããªãäœæããŠæåã®ãã¹ããèšè¿°ããŸãã
ãããŠããããŒã«ãã䜿çšããŠãã¹ããéå§ããå°ããªèšç»ãäœæããŸãã
-ãããŒã«ãã§ã¯ããŒãžé¢æ°ã§ããå¿ èŠããããŸã
-ããŒãžæ©èœã¯ã2ã€ã®ãªããžã§ã¯ãã1ã€ã«ããŒãžããå¿ èŠããããŸã
-ããã«ãæåã«éä¿¡ããããªããžã§ã¯ãã¯å±éããå¿ èŠãããã2çªç®ã®ãªããžã§ã¯ãã¯
-é¢æ°ã¯2çªç®ã®ãªããžã§ã¯ããå€æŽããŠã¯ãªããŸãã
Mochaã®BDDãã¹ãã¯ã describeïŒïŒãããã¯ã§å§ãŸããŸãã ããã¹ãèªäœã¯itïŒïŒãããã¯ã«æžã蟌ãŸããŸãã describeïŒïŒãããã¯å ã«é 眮ããå¿ èŠããããŸãã describeïŒïŒãããã¯ãçžäºã«ãã¹ãããããšãã§ããŸãã ããã¯ã䜿çšã§ããŸãïŒbeforeïŒïŒãafterïŒïŒãbeforeEachïŒïŒãafterEachïŒïŒã ããã¯ã¯ãdescribeïŒïŒãããã¯å ã«ãèšè¿°ããå¿ èŠããããŸãã åœã®ããŒã¿ããŒã¹ãæäœããã¢ãã«ããã¹ããããšãã®ããã¯ã«ã€ããŠè©³ãã説æããŸãã
ãã¹ããã£ã¬ã¯ããªã§ãtools.jsãã¡ã€ã«ãäœæããtools.mergeã®ãã¹ããèšè¿°ããŸãã
test / tools.js
var tools = require('../lib/tools/index.js'); describe('tools', function () { // "" merge it('should be have #merge', function () { tools.should.be.have.property('merge'); tools.merge.should.be.a('function'); }); describe('#merge', function () { // merge it('should merged', function () { var a = { foo: '1' }, b = { bar: '2' }; tools.merge(a, b).should.eql({ foo: '1', bar: '2' }); }); // , it('should be extend', function () { var a = { foo: '1' }, b = { bar: '2' }; tools.merge(a, b); // , // a.should.not.equal({ foo: '1', bar: '2' }); a.should.equal(a); }); // it('should not be extended', function () { var a = { foo: '1' }, b = { bar: '2' }; tools.merge(a, b); b.should.not.eql({ foo: '1', bar: '2' }); }); }); });
ããã§ãã¹ããå®è¡ãããšãããŒã«ã¢ãžã¥ãŒã«ãæ¥ç¶ãã段éã§ãšã©ãŒã«é¥ããŸãããããã¯æ£åžžã§ããããã®ã¢ãžã¥ãŒã«ã¯ãŸã ãããŸããã ãã¡ã€ã«lib / tools / index.jsãäœæããããã«lib / app.jsããããŒãžé¢æ°ã³ãŒãã転éããŸãã
makeãã¹ããå®è¡ãã4ã€ã®ãã¹ããã¹ãŠãå§åãããããšã確èªããŸãã
åç
ãªããªã æåã®ãã¹ããå§åããããšãããŒãžæ©èœãããŒã«ã¢ãžã¥ãŒã«ãããšã¯ã¹ããŒããããªãããšãæããã«ãªããŸãã ãšã¯ã¹ããŒããè¿œå ãããã¹ããå床å®è¡ãããšããã¹ãŠãæ£åžžã«åäœããããã«ãªããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®æ®ããããã«ãã¹ãããåã«ãã«ãã¬ããžãã¹ããè¿œå ããŸãã
ãã©ã¡ãŒã¿ãŒ--encoding = utf8ããã³--no-highlightã 䜿çšããŠjscoverageã®å®è¡ãè¿œå ããçä¿¡ãã£ã¬ã¯ããªãŒãšããŠlibããçºä¿¡ãã£ã¬ã¯ããªãŒãšããŠlib- covãæå®ããŸãã ããã§ãã«ãã¬ããžãã¹ãã®Mochaèµ·åãè¿œå ããç°å¢å€æ°COVERAGE = 1ãèšå®ããã¬ããŒã¿ãŒãšããŠhtml-covãæå®ããŠãã«ãã¬ããžãã¹ãã®çµæãå«ãçŸããhtmlããŒãžãååŸããŸãã
ã¡ã€ã¯ãã¡ã€ã«
MOCHA = ./node_modules/.bin/mocha test: @NODE_ENV=test $(MOCHA) \ -r should \ -R spec test-cov: lib-cov @COVERAGE=1 $(MOCHA) \ -r should \ -R html-cov > coverage.html lib-cov: clear @jscoverage --encoding=utf8 --no-highlight lib lib-cov clear: @rm -rf lib-cov coverage.html .PHONY: test test-cov clear
ãã¹ãã«æ»ã£ãŠãäžçªäžã®è¡ã眮ãæããŸãããã
var tools = require('../lib/tools/index.js');
ã«
var tools = process.env.COVERAGE ? require('../lib-cov/tools/index.js') : require('../lib/tools/index.js');
ããã ãã§ã ããã§make test-covãå®è¡ã§ããŸãã coverage.htmlãã¡ã€ã«ããããžã§ã¯ãã«ãŒãã«è¡šç€ºãããŸããã«ãã¬ããžãã¹ãã®çµæããã¡ã€ã«ã¯èªå·±å®çµåã§ããããã©ãŠã¶ã§ããã«éãããšãã§ããŸãã
åç
ãšã³ããªã1ã€ããªãã£ãè¡ã¯èµ€ã§è¡šç€ºãããŸããã€ãŸãããã®å Žæã¯ãã¹ãã®å¯Ÿè±¡å€ã§ãã åãã¡ã€ã«ã®å²åãšããŠã®ãã¹ãç¯å²ã®äžè¬çãªçµ±èšãæäŸãããŸãã
ããŠããã¹ãç°å¢ã¯æ§æãããŠããŸãããããŒã¿ããŒã¹ãšãµãŒããŒã®ãã¹ããèšè¿°ããããšã¯æ®ã£ãŠããŸãã
ããŒã¿ããŒã¹ã§ã®äœæ¥ã®ãã¹ã
ã¢ãã«ããã¹ãããããã®ã³ãŒããæžããŸãã ãŸããæ©èœã決å®ããŸãããã
1ïŒUserãšUserListã®2ã€ã®ã¢ãã«ãå¿ èŠã§ã
2ïŒãŠãŒã¶ãŒã¢ãã«ã«ã¯ã¡ãœãããå¿ èŠã§ãã
-find-é¢æ°ã¯ãUserListåã®ãªããžã§ã¯ããæã€ãŠãŒã¶ãŒã®ãªã¹ããè¿ããŸãïŒäœããªãå Žåã§ãïŒ
-findById-é¢æ°ã¯IDã§ãŠãŒã¶ãŒãæ€çŽ¢ããçµæãUseråã®ãªããžã§ã¯ããšããŠè¿ããããã®IDãæã€ãŠãŒã¶ãŒãããªãå Žåã¯äœãè¿ããªã
-ä¿å-é¢æ°ã¯ãŠãŒã¶ãŒãä¿åããå¿ èŠãããããšã©ãŒã®å Žåã¯errãè¿ããŸã
-toJSON-é¢æ°ã¯ãUseråã®ãªããžã§ã¯ããjsonã«ãã£ã¹ããã
3ïŒUserListã¢ãã«ã«ã¯toJSONã¡ãœããã®ã¿ãå¿ èŠã§ã
ãã¹ãã³ãŒã
var should = require('should'), db = process.env.COVERAGE ? require('../lib-cov/models/db.js') : require('../lib/models/db.js'), models = process.env.COVERAGE ? require('../lib-cov/models/index.js') : require('../lib/models/index.js'), User = models.User, UserList = models.UserList; describe('models', function () { // // "describe('models')" before(function () { db.regen(); }); // User it('should be have User', function () { models.should.be.have.property('User'); models.User.should.be.a('function'); }); // UserList it('should be have UserList', function () { models.should.be.have.property('UserList'); models.UserList.should.be.a('function'); }); // User describe('User', function () { // User find it('should be have #find', function () { User.should.be.have.property('find'); User.find.should.be.a('function'); }); // User findById it('should be have #findById', function () { User.should.be.have.property('findById'); User.findById.should.be.a('function'); }); // User save it('should be have #save', function () { User.prototype.should.be.have.property('save'); User.prototype.save.should.be.a('function'); }); // User toJSON it('should be have #toJSON', function () { User.prototype.should.be.have.property('toJSON'); User.prototype.toJSON.should.be.a('function'); }); describe('#find', function () { //find UserList it('should be instanceof UserList', function (done) { User.find(function (err, list) { if (err) return done(err); list.should.be.an.instanceOf(UserList); done(); }); }); //find UserList, it('should not be exist', function (done) { // db.drop(); User.find(function (err, list) { // db.generate(); if (err) return done(err); list.should.be.an.instanceOf(UserList); done(); }); }); }); describe('#findById', function () { //findById User it('should be instanceof User', function (done) { User.findById(0, function (err, user) { if (err) return done(err); user.should.be.an.instanceOf(User); done(); }); }); //findById , it('should not be exists', function (done) { User.findById(100, function (err, user) { if (err) return done(err); should.not.exist(user); done(); }); }); }); describe('#save', function () { //save , it('should not be saved', function (done) { var user = new User({ name: 'New user', age: 0, sex: 'w' }); user.save(function (err) { err.should.eql('Invalid age'); done(); }); }); // , it('should be saved', function (done) { var newuser = new User({ name: 'New user', age: 2, sex: 'w' }); newuser.save(function (err) { if (err) return done(err); User.findById(newuser.id, function (err, user) { if (err) return done(err); user.should.eql(newuser); done(); }); }); }); }); describe('#toJSON', function () { //toJSON json it('should be return json', function (done) { User.findById(0, function (err, user) { if (err) return done(err); user.toJSON().should.be.eql({ id: 0, name: 'Jo', age: 20, sex: 'm' }); done(); }); }); }); }); describe('UserList', function () { //UserList toJSON it('should be have #toJSON', function () { UserList.prototype.should.be.have.property('toJSON'); UserList.prototype.toJSON.should.be.a('function'); }); }); });
ã³ãŒãã«ã¯ã³ã¡ã³ããä»ããŠãããããç¹å®ã®ãã€ã³ãã«ã®ã¿çŠç¹ãåœãŠãŸãã
before(function () { db.regen(); });
ãã®ã³ãŒãã¯ããã¹ãã®éå§æã«äžåºŠåŒã³åºãããŸãã ããã§ã¯ãããŒã¿ããŒã¹ã«æ¥ç¶ããŠãã¹ãããŒã¿ãå ¥åã§ããŸããå®éã®ããŒã¿ããŒã¹ã¯ãªããããregenã¡ãœãããåŒã³åºãã ãã§ãããŒã¿ããŒã¹ããã¹ãããŒã¿ã§åæåããŸãã
ããŒã¿ããŒã¹ã§ã®äœæ¥ã¯éåæã¹ã¿ã€ã«ã§å®è¡ããããšããäºå®ã«æ³šæãæã䟡å€ããããŸããéåæã¡ãœããããã¹ããããšãããããã¯ãã¹ããå®äºããããã«doneïŒïŒã¡ãœãããåŒã³åºãå¿ èŠããããŸãããšã©ãŒã®å Žåããšã©ãŒãæž¡ãå¿ èŠããããŸãã æ確ã«ããããã®ã³ãŒãïŒ
... //find UserList it('should be instanceof UserList', function (done) { User.find(function (err, list) { if (err) return done(err); list.should.be.an.instanceOf(UserList); done(); }); }); ...
ããã§ã¯å§ããŸãããã libãã£ã¬ã¯ããªã§ãã¢ãã«ãæäœããããã®æ©èœãå®è£ ãããã¢ãã«ãã£ã¬ã¯ããªãäœæããŸãã
ã¢ãã«/ db.js
/* * */ // var users = []; exports.users = users; // exports.regen = function () { exports.drop(); exports.generate(); }; exports.drop = function () { // , // users.splice(0, users.length); }; exports.generate = function () { // users.push({ id: 0, name: 'Jo', age: 20, sex: 'm' }); users.push({ id: 1, name: 'Bo', age: 19, sex: 'm' }); users.push({ id: 2, name: 'Le', age: 18, sex: 'w' }); users.push({ id: 10, name: 'NotFound', age: 18, sex: 'w' }); }; // exports.generate();
ã¢ãã«/ user.js
var util = require('util'), db = require('./db.js'), UserList = require('./userlist.js'), users = db.users; /* * */ var User = module.exports = function User(opt) { this.id = users.length; this.name = opt.name; this.age = opt.age; this.sex = opt.sex; this.isNew = true; } /* * */ function loadFromObj(obj) { var user = new User(obj); user.id = obj.id; user.isNew = false; return user; } /* * */ User.find = function (fn) { var i, l = users.length, list; if (l) { list = new UserList(); for (i = 0, l; l > i; i += 1) { list.push(loadFromObj(users[i])); } } fn(null, list); }; /* * id */ User.findById = function (id, fn) { var obj = users[id], user; if (obj) { user = loadFromObj(obj); } fn(null, user); }; /* * */ User.prototype.save = function (fn) { var err; // if (Number.isFinite(this.age) && this.age > 0 && this.age < 150) { if (this.isNew) { users.push(this.toJSON()); this.isNew = false; } else { users[this.id] = this.toJSON(); } } else { err = 'Invalid age'; } fn(err); }; User.prototype.toJSON = function () { var json = { id: this.id, name: this.name, age: this.age, sex: this.sex }; return json; };
ã¢ãã«/ userlist.js
var util = require('util'); /* * UserList - , Array */ var UserList = module.exports = function UserList() { Array.apply(this) } util.inherits(UserList, Array); UserList.prototype.toJSON = function () { var i, l = this.length, arr = new Array(l); for (i = 0; l > i; i += 1) { arr[i] = this[i].toJSON(); } return arr; };
ã¢ãã«/ index.js
exports.User = require('./user.js'); exports.UserList = require('./userlist.js');
ãŠãŒã¶ãŒã¢ãã«æ¥ç¶ãè¿œå ããããšã§lib / app.jsã³ãŒãã調æŽãããããä»ããŠãŠãŒã¶ãŒãšã®ãã¹ãŠã®äœæ¥ãè¡ããŸãã
lib / app.js
var ... User = require('./models/index.js').User, ... ... app.get('/users', function(req, res, next){ User.find(function (err, users) { if (err) { next(err); } else { res.render('index', { users: users.toJSON() }); } }); }); app.get('/users/profile', function(req, res, next){ var id = req.query.id; User.findById(id, function(err, user) { if (user) { res.render({ user: user.toJSON() }); } else { next('Not found'); } }); }); ...
ã¢ããªã±ãŒã·ã§ã³ããã¹ããã
ãã¹ãã§ã«ããŒãããªãæåŸã®éšåããããŸããã ããã¯çŽæ¥HTTPãµãŒããŒã§ãã ããã§ã¯ã4ã€ã®ç¶æ³ã®ã¿ãããªãŒãºããŠãã¹ãããããšã«ããŸããã
1ïŒãããéåžžã®ãªã¯ãšã¹ãã§ããå Žåãçãã¯htmlã«æ¥ãå¿ èŠããããŸã
2ïŒãããajaxã®å Žåãçãã¯jsonã«æ¥ãå¿ èŠããããŸã
3ïŒãµã€ãã®ã«ãŒããžã®GETãªã¯ãšã¹ãã¯ãã¿ã€ãã«ã«å€ãMy siteããå«ãŸããããŒãž/ãªããžã§ã¯ããè¿ãå¿ èŠããããŸã
ã¹ãŒããŒãã¹ãã©ã€ãã©ãªã®ãããã§ããã®ãããªãã¹ãã®äœæã¯ç°¡åã§ç°¡åã§ãã
test / app.js
var request = require('supertest'), app = process.env.COVERAGE ? require('../lib-cov/app.js') : require('../lib/app.js'); describe('Response html or json', function () { // , // html it('should be responded as html', function (done) { request(app) .get('/') .expect('Content-Type', /text\/html/) .expect(200, done); }); // , json it('should be responded as json', function (done) { request(app) .get('/') .set('X-Requested-With', 'XMLHttpRequest') .expect('Content-Type', /application\/json/) .expect(200, done); }); }); describe('GET /', function () { // title === it('should be included title', function (done) { request(app) .get('/') .end(function (err, res) { if (err) return done(err); res.text.should.include('<title> </title>'); done(); }); }); // title === it('should be included title', function (done) { request(app) .get('/') .set('X-Requested-With', 'XMLHttpRequest') .end(function (err, res) { if (err) return done(err); res.body.should.have.property('data'); res.body.data.should.have.property('title', ' '); done(); }); }); });
requestïŒïŒã§ãhttp.Serverã®ã€ã³ã¹ã¿ã³ã¹ãŸãã¯èŠæ±ãå®è¡ããé¢æ°ãæž¡ãå¿ èŠããããŸãã SuperTestã¯SuperAgentã䜿çšããŠãµãŒããŒãšå¯Ÿè©±ããããããã¹ãŠã®æ©èœã䜿çšããŠãµãŒããŒãžã®ãªã¯ãšã¹ããäœæã§ããŸãã å¿çã®æ€èšŒã¯ã expectïŒïŒé¢æ°ã§ããŸãã¯ãã³ãã©ãŒé¢æ°ãendïŒïŒã«æž¡ãããšã§èŠæ±ã®çµæãšããŠçŽæ¥å®è¡ã§ããŸãã
ãããã«
ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ããæžãã®ã¯ãšãŠãç°¡åã§ãïŒãããŠå¿ èŠã§ããïŒã ç§ã®ãã¹ãçšã®å°ããªãµã³ãã«ã³ãŒãã§ãããã¹ãã³ãŒãèªäœãããå€ãã®ããšãå€æããŸãããããããã®ãã¹ãã§ããäžå®å šã§ãããããšãã°ã2人ã®ãŠãŒã¶ãŒãåæã«äœæããŠä¿åããå Žåããšã©ãŒã¯ãã¹ãã§ã«ããŒãããŸããã ã«ãã¬ããžãã¹ãã¯ããŠãŒã¶ãŒã¢ãã«ãæ°ãããŠãŒã¶ãŒãä¿åãããå Žæã®ãã¹ãã§ã«ããŒãããããšã瀺ããŠããŸããã
ãããã£ãŠããã¹ãèªäœã¯äžèœè¬ã§ã¯ãããŸããããã¹ãã¯æ£ããèšè¿°ãããŠããå¿ èŠããããããããªããšãç解ããåé¡ãåŒãèµ·ããå¯èœæ§ã®ããå Žæãæ£ç¢ºã«ãã¹ãããå¿ èŠããããŸãã
ã³ãŒãã¯githubã§å ¥æã§ããŸã