Slide 1

Slide 1 text

Writing testable private methods with Node.js modules Mark S. Everitt (@qubyte) June 19, 2014 @ Async

Slide 2

Slide 2 text

Why test? • Assert behaviour. Publish with confidence. • Maintain behaviour. Refactor with confidence. • Add tests after bugfixes. Avoid regressions.

Slide 3

Slide 3 text

Why use private methods? • Small API surface. Less for users to learn. • Protect internal implementation to allow refactoring.

Slide 4

Slide 4 text

But... Writing private methods is a little tricky.

Slide 5

Slide 5 text

Modules help us out! function AsyncAttendee(name) { this.name = name; } // Make the module a reference to the constructor. module.exports = AsyncAttendee; // "private" methods take context as the zeroth argument. function sayHello(asyncAttendee) { return 'Hello ' + asyncAttendee.name + '!'; } AsyncAttendee.prototype.hello = function () { console.log(sayHello(this)); };

Slide 6

Slide 6 text

But... What about testing that private method?

Slide 7

Slide 7 text

Option 1 function AsyncAttendee(name) { this.name = name; } // Make the module a reference to the constructor. module.exports = AsyncAttendee; // "private" methods are prefixed with _ . AsyncAttendee.prototype._sayHello = function () { return 'Hello ' + this.name + '!'; }; AsyncAttendee.prototype.hello = function () { console.log(this.sayHello()); };

Slide 8

Slide 8 text

Option 1 Don’t do this.

Slide 9

Slide 9 text

Option 1 Users won’t respect convention!

Slide 10

Slide 10 text

Option 2 Use a module. • core vm module • sandboxed-module

Slide 11

Slide 11 text

Option 2 Lots of magic going on.

Slide 12

Slide 12 text

Option 3 AsyncAttendee.js

Slide 13

Slide 13 text

Option 3 AsyncAttendee index.js

Slide 14

Slide 14 text

Option 3 AsyncAttendee index.js private.js

Slide 15

Slide 15 text

Option 3 // index.js var privateMethods = require('./private'); function AsyncAttendee(name) { this.name = name; } module.exports = AsyncAttendee; AsyncAttendee.prototype.hello = function () { console.log(privateMethods.sayHello(this)); }; // private.js exports.sayHello = function (asyncAttendee) { return 'Hello ' + asyncAttendee.name + '!'; };

Slide 16

Slide 16 text

Option 3 Use a namespace module. • "Public" to the filesystem (and therefore tests). • "Private" to JS outside your module.

Slide 17

Slide 17 text

Thank you for your attention! Pop Quiz: What does return do in a function called with new?