34
Testable Javascript MARK ETHAN TROSTLER mark @zzo. com @zzoass http://randomgoo.blogspot.com/

Testable Javascript

Embed Size (px)

DESCRIPTION

Writing testable javascript preso given at YUIConf 2011 (11/3/11) - good times!!!

Citation preview

Page 1: Testable Javascript

Testable Javascript

MARK ETHAN TROSTLER [email protected]@zzoass

http://randomgoo.blogspot.com/

Page 2: Testable Javascript

“If today were the last day of my life, would I want to do what I am about to do today?”

Page 3: Testable Javascript

KISS

Page 4: Testable Javascript

Minimize Your Pain

•Write Small•Write Simple•Test Early•Test Often

Page 5: Testable Javascript

Write Small – Write Simple

•Isolate code to test•Loosely couple dependencies•Be obvious•Optimize last

Page 6: Testable Javascript

Test Early – Test Often

Test Now

Page 7: Testable Javascript

Explicit Dependencies

Page 8: Testable Javascript

How things can go wrong…

April 12, 2023

// Call x.flub with twice zfunction foo(z) {

var x = new X(); // Mmmmm tightly coupled…

return x.flub(z*2);}

// We want to test if x.flub was called with 14var back = foo(7);// But only have ‘back’ to test!

Page 9: Testable Javascript

April 12, 2023

How can we test only our code?

// Call x.flub with twice zfunction foo(x, z) {

return x.flub(z*2);}

// Test x.flub got called with 14…var x = new MockX(), foo(x, 7);

If (x.called(‘flub’).with(14)) { // a winner!!}

Page 10: Testable Javascript

Really see this in constructors…

April 12, 2023

// Ewww – not directly testablefunction MyObject() {

this.x = new X();}

MyObject.Prototpe.foo = function(z) {return this.x.flub(z*2);

}

var test = new MyObject(), back = test.foo(7) // cannot test what foo() did

Page 11: Testable Javascript

Inject It - Constructor

April 12, 2023

// Yea! Testable!function MyObject(x) {

this.x = x;}MyObject.Prototpe.foo = function(z) {

return this.x.flub(z*2);}

var x = new MockX(), test = new MyObject(x);

test.foo(7);If (x.called(‘flub’, with(14)) { // We tested only our function!}

Page 12: Testable Javascript

Inject It - Setter

April 12, 2023

// Yea! Testable!function MyObject() { }MyObject.prototpe.setX= function(x) { this.x = x;};MyObject.prototpe.foo = function(z) {

return this.x.flub(z*2);};var x = new MockX(), test = new MyObject();test.setX(x);test.foo(7);If (x.called(‘flub’, with(14)) { // We tested only our function!}

Page 13: Testable Javascript

YUI

Page 14: Testable Javascript

YUI Explicit Dependencies

April 12, 2023

YUI.add(‘myObject’, function(Y) { // Code you want to test Y.MyObject = function() { this.a = new Y.a(); this.b = new Y.b(); this.c = new Y.c(); };}, { requires: [ ‘a’, ‘b’, ‘c’ ] });

YUI.use(‘myObject’, function(Y) { new Y.MyObject();});

Page 15: Testable Javascript

Injecting YUI3 Dependencies??

April 12, 2023

YUI.add(‘myObject’, function(Y) { //// Code you want to test Y.MyObject = function(a, b, c) { this.a = a; this.b = b; this.c = c; };});YUI.use(‘myObject’, ‘a’, ‘b’, ‘c’, function(Y) { // Prod new Y.MyObject(new Y.a(), new Y.b(), new Y.c());});YUI.use(‘myObject’, ‘test’, function(Y) { // Test new Y.MyObject(Y.Mock(), Y.Mock(), Y.Mock());});

Page 16: Testable Javascript

Isolating Dependencies

April 12, 2023

<script src =“yui3.js”></script><script src =“a.mock.js”></script><script src =“b.mock.js”></script><script src =“c.mock.js”></script><script src =“myObject.js”></script>

<script>YUI.use(‘myObject’, function(Y) { new Y.MyObject();</script>

Page 17: Testable Javascript

Injecting YUI3 Dependencies

April 12, 2023

YUI.add(‘myObject’, function(Y) { //// Code you want to test Y.MyObject = function(a, b, c) { this.a = a || new Y.a(); this.b = b || new Y.b(); this.c = c || new Y.c(); };}, { requires: [‘a’, ‘b’, ‘c’ ] });YUI.use(‘myObject’, function(Y) { // Prod new Y.MyObject();});YUI.use(‘myObject’, ‘test’, function(Y) { // Test new Y.MyObject(Y.Mock(), Y.Mock(), Y.Mock());});

Page 18: Testable Javascript

Injecting YUI3 Dependencies

April 12, 2023

YUI.add(‘myObject’, function(Y) { //// Code you want to test Y.MyObject = function() { } Y.MyObject.prototype.setX = function(x) { this.x = x; };}, { requires: [‘a’, ‘b’, ‘c’ ] });YUI.use(‘myObject’, function(Y) { // Prod new Y.MyObject();});YUI.use(‘myObject’, ‘test’, function(Y) { // Test new Y.MyObject().setX(Y.Mock());});

Page 19: Testable Javascript

Isolating Dependencies

YUI({ modules: { a: { fullpath: ’/a-mock.js'

} }}).use('test', ’myObject', 'console',

function(Y) { var obj = new Y.MyObject();});

April 12, 2023

Page 20: Testable Javascript

Isolating Dependencies

YUI({ filter: mock:

{ 'searchExp': “\\.js", 'replaceStr': "-mock.js" }}).use('test', ’myObject', 'console',

function(Y) { var obj = new Y.MyObject();});

April 12, 2023

Page 21: Testable Javascript

Mock Objects

Page 22: Testable Javascript

Mock Object

YUI().add(’a', function(Y) { Y.A= function() { var me = Y.Mock(); Y.Mock.expect(me, { method: ”render",

args: [‘#toolbar’] }); return me; }}, '1.0.0' ,{requires:['test']});

April 12, 2023

Page 23: Testable Javascript

Testing with Mocked Dependencies

YUI().add(’a', function(Y) { Y.A= function() { return Y.Mock(); };}, '1.0.0' ,{requires:['test']});YUI().use(‘a’, ‘test’, ‘myObject’, function(Y) { var a = new Y.A(); Y.Mock.expect(a, { method: ”render",

args: [‘#toolbar’] }); new MyObject(a).render(); //// verify a.render(‘#toolbar’) was called});

April 12, 2023

Page 24: Testable Javascript

Implicit Dependencies

Page 25: Testable Javascript

‘Hidden’ Dependencies

Private functions are ‘hidden’ dependencies

Cannot test by definition! Make public? Are they part of the public

API?? Mock them out?? How??

Refactor private function to be explicit dependencies

Minimize their use – they cannot be tested directly

Treated exactly like any other dependency

April 12, 2023

Page 26: Testable Javascript

Private functions

April 12, 2023

YOU CANNOT TEST THEM!

Page 27: Testable Javascript

Don’t forget browser dependencies!

YUI.add(‘myObject’, function(Y) { //// Code you want to test Y.MyObject = function(window) { this.window = window; }; myObject.prototype.fiddle = function(str) { return

window.escape(str + ‘ FOOBIE’); }; });

YUI.use(‘myObject’, function(Y) { var winMock = new WindowMock(), myObj = new myObject(winMock); myObj.fiddle(‘foobie’); // Check WindowMock!!}

April 12, 2023

Page 28: Testable Javascript

Recap

Page 29: Testable Javascript

Explicit Deps

Problem: Public dependenciesSymptom: Explicitly declared

dependenciesCure: • Eliminate tightly coupled code/use

injection• Pre-load mock’ed versions of public

dependencies

April 12, 2023

Page 30: Testable Javascript

Private Deps

Problem: Private dependenciesSymptom: Private methods and

functionsCure: • Minimize private dependencies• Make public dependency• Use composition to pull in private stuff

April 12, 2023

Page 31: Testable Javascript

Browser Deps

Problem: Browser dependenciesSymptom: ‘window’ or global scope

usageCure: • Eliminate tightly coupled code/use

injection• Pre-load mock’ed versions of public

dependencies

April 12, 2023

Page 32: Testable Javascript

Unit Testing Principles & Practices

All about dependency management

Identify dependencies and mock them out

Do not take your environment for granted

April 12, 2023

Page 33: Testable Javascript

“Don’t Be Foolish”

Page 34: Testable Javascript

Resources

• https://github.com/zzo/JUTE• [email protected]• @zzoass• http://randomgoo.blogspot.com/

April 12, 2023