SlideShare una empresa de Scribd logo
1 de 46
Descargar para leer sin conexión
Testable JavaScript
Architecting Your Application for
Testability
Mark Ethan Trostler
Google Inc.
twitter: @zzoass
mark@zzo.com
Loose
Coupling
Tight
Focus
No
Surprises
What Is Testability?
Minimal
Tedium
Swap
Implementations
Work/Test in
Parallel
Interfaces not Implementation
Write Tests
Once
Interfaces not Implementation
var UserRepository = {
get: function(id) {}
, save: function(user) {}
, getAll: function() {}
, edit: function(id, user) {}
, delete: function(id) {}
, query: function(query) {}
};
Interfaces not Implementation
function test(repo) {
var id = 99, user = { id: id, ... };
repo.save(user);
expect(repo.get(id)).toEqual(user);
}
Test the interface
Interfaces not Implementation
var UserRepoRedis = function(host, port, opt) {
this.redis = redis.createClient({ ... });
};
UserRepoRedis.prototype =
Object.create(UserRepo);
UserRepoRedis.prototype.save =
function(user) { ... };
Interfaces not Implementation
• Object is interface - no initialization
• Implementation has constructor with
injected dependencies
• Prototype is Object.create(Interface)
• Override with prototype functions
Interfaces not Implementation
function test(repo) {
var user = { ... };
repo.save(user);
expect(repo.get(id)).toEqual(user);
}
var repo = new UserRepoRedis(host, port, opt);
test(repo);
Test the interface
Interfaces not Implementation
var UserRepoS3 = function(key, secret, bucket) {
this.s3 = knox.createClient({...});
};
UserRepoS3.prototype = Object.create(UserRepo);
UserRepoS3.prototype.save =
function(user) { ... }
Interfaces not Implementation
function test(repo) {
var id = 99, user = { id: id, ... };
repo.save(user);
expect(repo.get(id)).toEqual(user);
}
var repo = new UserRepoS3(key, secret, bucket);
test(repo);
Test the interface
Single Responsibility Principle
Every interface should have a single
responsibility, and that responsibility
should be entirely encapsulated by
the interface.
Responsibility = Reason To Change
Interface Segregation Principle
No object should be forced
to depend on methods it
does not use.
Interface Pattern
• Single Responsibility Principle
• Match Sets with Gets
• More Smaller / Fewer Bigger
• Interface Segregation Principle
• Test and Program to Interface Only
Using Interfaces
You've created nice interfaces - use them
wisely!
// DO NOT DO THIS!
var UserController = function() {
this.userRepo = new UserRepoRedis();
};
Using Interfaces
You've created nice interfaces - use them
wisely!
// DO THIS - Inject the dependencies!
var UserController = function(userRepo) {
this.userRepo = userRepo;
};
Liskov Substitution Principle
Any objects that implement
an interface can be used
interchangeably
Constructor Injection
All dependencies should be injected into your
object's constructor*.
•  Make dependencies explicit
•  Loose coupling
•  Cannot instantiate a non-usable Object
* Except:
•  runtime dependencies
•  objects with a shorter lifetime
Instantiating Implementations
So do all dependees need to provide fully
initialized objects to their dependents when
instantiating them?
NO - THEY DO NOT INSTANTIATE THEM!
Object Creation vs. Object Use
•  ensures a loosely coupled system
•  ensures testability
Object Creation vs. Object Use
creation
Happens one time at the Composition Root.
All Objects* are created/wired together at
this time
•  startup
•  Request
use
Happens all the time throughout the application
Object Creation
What creates the objects?
Forces specification of dependencies and
their lifespan
•  DIY DI
•  wire.js / cujojs
•  Inverted
•  Intravenous
•  AngularJS
whole lotta others...
Cross-Cutting Concerns
What about the crap I might need?
•  Logging
•  Auditing
•  Profiling
•  Security
•  Caching
•  ....
"Cross-cutting concerns"
Cross-Cutting Concerns
// DO NOT DO THIS:
UserRepoRedis = function(...) {
this.redis = ...
this.logger = new Logger(); // or Logger.get()
this.profiler = new Profiler(); // or Profiler.get()
};
•  tightly coupled to Logger & Profiler
implementations
•  PITA to test
Cross-Cutting Concerns
// DO NOT DO THIS:
UserRepoRedis = function(..., logger, profiler) {
this.redis = ...
this.logger = logger;
this.profiler = profiler;
};
Injection - so looks good BUT violating SRP!
Cross-Cutting Concerns
// DO NOT DO THIS:
UserRepoRedis.prototype.save =
function(user) {
logger.log('Saving user: ' + user);
profiler.startProfiling('saveUser');
... do redis/actual save user stuff ...
profiler.stopProfiling('saveUser');
logger.log('that took: ' + (start - end));
};
Cross-Cutting Concerns
•  Keep different functionality separate
•  Single Responsibility Principle
•  Interface Segregation Principle
var LoggerFile = function(file) { // Implementation
this.file = file;
}
LoggerFile.prototype = Object.create(Logger);
LoggerFile.prototype.log = function(msg) {
this.file.write(msg);
};
Logging Interface and Implementation
var Logger = { // Interface
log: function(msg) {}
, getLastLog: function() {} // get it out!
};
Mixin method - quick aside...
// Composition not inheritance
var MIXIN = function(base, extendme) {
var prop;
for (prop in base) {
if (typeof base[prop] === 'function'
&& !extendme[prop]) {
extendme[prop] = base[prop].bind(base);
}
}
};
Decorator
var UserRepoLogger = function(repo, logger) {
this.innerRepo = repo;
this.logger = logger;
MIXIN(repo, this); // Mixin repo's methods
};
UserRepoLogger.prototype.save =
function(user) {
this.logger.log('Saving user: ' + user);
return this.innerRepo.save(user);
};
Decorator
// UserRepoLogger will Intercept save
// all other methods will fall thru to
// UserRepoRedis
// This userRepo implements the UserRepo
// interface therefore can be used anywhere
var userRepo =
new UserRepoLogger(
new UserRepoRedis()
, new LoggerFile()
);
var ProfilerTime = function() { this.profiles = {}; };
ProfilerTime.prototype = Object.create(Profiler);
ProfilerTime.prototype.start = function(id) {
this.profiles[id] = new Date().getTimestamp();
};
ProfilerTime.prototype.stop = function(id) { ... };
....
Profile Interface and Implementation
var Profiler = { // Interface
start: function(id) {}
, stop: function(id) {}
, getProfile: function(id) {} // get it out!
};
Decorator
var UserRepoProfiler = function(repo, profiler) {
this.innerRepo = repo;
this.profiler = profiler;
MIXIN(repo, this); // Mixin repo's methods
};
UserRepoProfiler.prototype.save = f(user) {
this.profiler.start('save user');
this.innerRepo.save(user);
this.profiler.stop('save user');
Intercept
var redisRepo = new UserRepoRedis();
var profiler = new ProfilerTime();
var logger = new LoggerFile();
// Profiling UserRepo
var userRepoProf =
new UserRepoProfiler(redisRepo, profiler);
// Logging Profiling UserRepo
var userRepo =
new UserRepoLogger(userRepoProf, logger);
Testing Decorators
Create the Decorator and Test the Interface:
function testUserRepoLogger(repo) {
var id = 99, user = { id: id, ... },
loggerMock = new LoggerMock(),
testRepo =
new UserRepoLogger(repo, loggerMock);
testRepo.save(user);
// verify loggerMock
}
Testing Decorators
var repo = new UserRepoMock();
var logger = new LoggerMock();
var profiler = new ProfileMock();
var userRepo = new UserRepoProfile(
new UserRepoLogger(repo, logger), profiler);
// test UserRepo interface
testRepo(userRepo);
// verify logger and profiler mocks
Decorator Pattern
•  Constructor accepts 'inner' Object of the
same type and any other necessary
dependencies.
•  Mixin with 'inner' Object to get default
behavior for non-decorated methods.
•  Decorate necessary interface methods and
(optionally) delegate to 'inner' Object.
var FindMatchesDistance = f(userRepo) { ... };
FindMatchesDistance.prototype =
Object.create(FindMatches);
var FindMatchesActivites = f(userRepo) { ... };
var FindMatchesLikes = f(userRepo) { ... };
Runtime/Short-lived Dependencies
var FindMatches = {
matches: function(userid) {}
};
Runtime/Short-lived Dependencies
// User Controller needs a findMatches
// implementation - but which one???
var UserController = function(findMatches, ...) {
....
}
Inject all three?? What if I make more? What if
other classes need a dynamic findMatch
implementation?
var FindMatchFactoryImp = function(repo) {
this.repo = repo;
};
FindMatchFactoryImpl.prototype =
Object.create(FindMatchFactory);
Runtime/Short-lived Dependencies
var FindMatchFactory = {
getMatchImplementation: function(type) {}
};
Runtime/Short-lived Dependencies
getMatchImplementation = function(type) {
switch(type) {
case 'likes':
return new FindMatchesLikes(this.repo);
break;
....
default:
return new FindMatchesActivites(this.repo);
}
};
Runtime/Short-lived Dependencies
var UserController = function(findMatchFactory) {
this.findMatchFactory = findMatchFactory;
};
UserController.prototype = Object.create(Controller);
UserController.prototype.findMatches = function(type, uid) {
var matcher =
this.findMatchFactory.getMatchImplementation(type);
return matcher.matches(uid);
};
Testing Abstract Factories
var matchTypes = [
{ name: 'likes', type: FindMatchesLikes }
, ....
];
test(findMatchFactory) {
matchTypes.forEach(function(type) {
expect(
findMatchFactory
.getMatchImplementation(type.name)
.toEqual(type.type);
});
}
Mocking Abstract Factories
var TestFactoryImpl = function(expectedType, mockMatch) {
this.expectedType = expectedType;
this.mockMach = mockMatch;
};
TestFactoryImpl.prototype = Object.create(FindMatchFactory);
TestFactoryImpl.prototype.getMatchImplementation(type) {
expect(type).toEqual(this.expectedType);
return this.mockMatch;
};
Abstract Factory Pattern
•  Abstract factory translates runtime
parameter -> Dependency
•  Factory implementation created at
Composition Root along with everything
else
•  Inject factory implementation into the
Constructor for runtime dependencies
•  Object uses injected factory to get
Dependency providing Runtime Value
Testable JavaScript
•  Composition not Inheritance (impl. lock-in)
•  Program and Test to Interfaces / Interfaces are
the API
•  Create lots of small Single Responsibility
Interfaces
•  Decorate and Intercept Cross-Cutting Concerns
•  Constructor Inject all Dependencies
•  Inject Abstract Factory for run-time
Dependencies
Ensuring Testability

Más contenido relacionado

La actualidad más candente

Javascript Prototype Visualized
Javascript Prototype VisualizedJavascript Prototype Visualized
Javascript Prototype Visualized军 沈
 
Javascript basics for automation testing
Javascript  basics for automation testingJavascript  basics for automation testing
Javascript basics for automation testingVikas Thange
 
Annotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark ArtsAnnotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark ArtsJames Kirkbride
 
Javascript closures
Javascript closures Javascript closures
Javascript closures VNG
 
Javascript basic course
Javascript basic courseJavascript basic course
Javascript basic courseTran Khoa
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJWORKS powered by Ordina
 
JavaScript Programming
JavaScript ProgrammingJavaScript Programming
JavaScript ProgrammingSehwan Noh
 
Object Oriented Programming in JavaScript
Object Oriented Programming in JavaScriptObject Oriented Programming in JavaScript
Object Oriented Programming in JavaScriptzand3rs
 
A Deeper look into Javascript Basics
A Deeper look into Javascript BasicsA Deeper look into Javascript Basics
A Deeper look into Javascript BasicsMindfire Solutions
 
Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMRafael Winterhalter
 
JavaScript - From Birth To Closure
JavaScript - From Birth To ClosureJavaScript - From Birth To Closure
JavaScript - From Birth To ClosureRobert Nyman
 
The JavaScript Programming Language
The JavaScript Programming LanguageThe JavaScript Programming Language
The JavaScript Programming LanguageRaghavan Mohan
 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011Nicholas Zakas
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascriptDoeun KOCH
 
Bytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASMBytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASMashleypuls
 
Design pattern is_everywhere_by_saurabh_sharma
Design pattern is_everywhere_by_saurabh_sharmaDesign pattern is_everywhere_by_saurabh_sharma
Design pattern is_everywhere_by_saurabh_sharmaSaurabh Sharma
 

La actualidad más candente (20)

Javascript Prototype Visualized
Javascript Prototype VisualizedJavascript Prototype Visualized
Javascript Prototype Visualized
 
Javascript basics for automation testing
Javascript  basics for automation testingJavascript  basics for automation testing
Javascript basics for automation testing
 
Annotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark ArtsAnnotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark Arts
 
Javascript closures
Javascript closures Javascript closures
Javascript closures
 
Spring boot
Spring bootSpring boot
Spring boot
 
Javascript
JavascriptJavascript
Javascript
 
Javascript basic course
Javascript basic courseJavascript basic course
Javascript basic course
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UX
 
JavaScript Programming
JavaScript ProgrammingJavaScript Programming
JavaScript Programming
 
Object Oriented Programming in JavaScript
Object Oriented Programming in JavaScriptObject Oriented Programming in JavaScript
Object Oriented Programming in JavaScript
 
EMF Tips n Tricks
EMF Tips n TricksEMF Tips n Tricks
EMF Tips n Tricks
 
A Deeper look into Javascript Basics
A Deeper look into Javascript BasicsA Deeper look into Javascript Basics
A Deeper look into Javascript Basics
 
Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVM
 
JavaScript - From Birth To Closure
JavaScript - From Birth To ClosureJavaScript - From Birth To Closure
JavaScript - From Birth To Closure
 
The JavaScript Programming Language
The JavaScript Programming LanguageThe JavaScript Programming Language
The JavaScript Programming Language
 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascript
 
Core concepts-javascript
Core concepts-javascriptCore concepts-javascript
Core concepts-javascript
 
Bytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASMBytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASM
 
Design pattern is_everywhere_by_saurabh_sharma
Design pattern is_everywhere_by_saurabh_sharmaDesign pattern is_everywhere_by_saurabh_sharma
Design pattern is_everywhere_by_saurabh_sharma
 

Similar a Testable JavaScript: Application Architecture

Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.jsdavidchubbs
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiRan Mizrahi
 
Annotation Processing
Annotation ProcessingAnnotation Processing
Annotation ProcessingJintin Lin
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 
Dev fest kyoto_2021-flutter_test
Dev fest kyoto_2021-flutter_testDev fest kyoto_2021-flutter_test
Dev fest kyoto_2021-flutter_testMasanori Kato
 
2. Design patterns. part #2
2. Design patterns. part #22. Design patterns. part #2
2. Design patterns. part #2Leonid Maslov
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 
TypeScript for Java Developers
TypeScript for Java DevelopersTypeScript for Java Developers
TypeScript for Java DevelopersYakov Fain
 
Concurrency and Thread-Safe Data Processing in Background Tasks
Concurrency and Thread-Safe Data Processing in Background TasksConcurrency and Thread-Safe Data Processing in Background Tasks
Concurrency and Thread-Safe Data Processing in Background TasksWO Community
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingVisual Engineering
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testingpleeps
 
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongJDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongPROIDEA
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeIan Robertson
 
OpenWebBeans and DeltaSpike at ApacheCon
OpenWebBeans and DeltaSpike at ApacheConOpenWebBeans and DeltaSpike at ApacheCon
OpenWebBeans and DeltaSpike at ApacheConos890
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js frameworkBen Lin
 
What's Coming in Spring 3.0
What's Coming in Spring 3.0What's Coming in Spring 3.0
What's Coming in Spring 3.0Matt Raible
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonCodemotion
 

Similar a Testable JavaScript: Application Architecture (20)

Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Android best practices
Android best practicesAndroid best practices
Android best practices
 
Annotation Processing
Annotation ProcessingAnnotation Processing
Annotation Processing
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Dev fest kyoto_2021-flutter_test
Dev fest kyoto_2021-flutter_testDev fest kyoto_2021-flutter_test
Dev fest kyoto_2021-flutter_test
 
2. Design patterns. part #2
2. Design patterns. part #22. Design patterns. part #2
2. Design patterns. part #2
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
TypeScript for Java Developers
TypeScript for Java DevelopersTypeScript for Java Developers
TypeScript for Java Developers
 
Concurrency and Thread-Safe Data Processing in Background Tasks
Concurrency and Thread-Safe Data Processing in Background TasksConcurrency and Thread-Safe Data Processing in Background Tasks
Concurrency and Thread-Safe Data Processing in Background Tasks
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Annotation processing tool
Annotation processing toolAnnotation processing tool
Annotation processing tool
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go WrongJDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
JDD 2016 - Grzegorz Rozniecki - Java 8 What Could Possibly Go Wrong
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive Code
 
OpenWebBeans and DeltaSpike at ApacheCon
OpenWebBeans and DeltaSpike at ApacheConOpenWebBeans and DeltaSpike at ApacheCon
OpenWebBeans and DeltaSpike at ApacheCon
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
ES6 metaprogramming unleashed
ES6 metaprogramming unleashedES6 metaprogramming unleashed
ES6 metaprogramming unleashed
 
What's Coming in Spring 3.0
What's Coming in Spring 3.0What's Coming in Spring 3.0
What's Coming in Spring 3.0
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - Warburton
 

Último

Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 

Último (20)

Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 

Testable JavaScript: Application Architecture

  • 1. Testable JavaScript Architecting Your Application for Testability Mark Ethan Trostler Google Inc. twitter: @zzoass mark@zzo.com
  • 3.
  • 5. Interfaces not Implementation var UserRepository = { get: function(id) {} , save: function(user) {} , getAll: function() {} , edit: function(id, user) {} , delete: function(id) {} , query: function(query) {} };
  • 6. Interfaces not Implementation function test(repo) { var id = 99, user = { id: id, ... }; repo.save(user); expect(repo.get(id)).toEqual(user); } Test the interface
  • 7. Interfaces not Implementation var UserRepoRedis = function(host, port, opt) { this.redis = redis.createClient({ ... }); }; UserRepoRedis.prototype = Object.create(UserRepo); UserRepoRedis.prototype.save = function(user) { ... };
  • 8. Interfaces not Implementation • Object is interface - no initialization • Implementation has constructor with injected dependencies • Prototype is Object.create(Interface) • Override with prototype functions
  • 9. Interfaces not Implementation function test(repo) { var user = { ... }; repo.save(user); expect(repo.get(id)).toEqual(user); } var repo = new UserRepoRedis(host, port, opt); test(repo); Test the interface
  • 10. Interfaces not Implementation var UserRepoS3 = function(key, secret, bucket) { this.s3 = knox.createClient({...}); }; UserRepoS3.prototype = Object.create(UserRepo); UserRepoS3.prototype.save = function(user) { ... }
  • 11. Interfaces not Implementation function test(repo) { var id = 99, user = { id: id, ... }; repo.save(user); expect(repo.get(id)).toEqual(user); } var repo = new UserRepoS3(key, secret, bucket); test(repo); Test the interface
  • 12. Single Responsibility Principle Every interface should have a single responsibility, and that responsibility should be entirely encapsulated by the interface. Responsibility = Reason To Change
  • 13. Interface Segregation Principle No object should be forced to depend on methods it does not use.
  • 14. Interface Pattern • Single Responsibility Principle • Match Sets with Gets • More Smaller / Fewer Bigger • Interface Segregation Principle • Test and Program to Interface Only
  • 15. Using Interfaces You've created nice interfaces - use them wisely! // DO NOT DO THIS! var UserController = function() { this.userRepo = new UserRepoRedis(); };
  • 16. Using Interfaces You've created nice interfaces - use them wisely! // DO THIS - Inject the dependencies! var UserController = function(userRepo) { this.userRepo = userRepo; };
  • 17. Liskov Substitution Principle Any objects that implement an interface can be used interchangeably
  • 18. Constructor Injection All dependencies should be injected into your object's constructor*. •  Make dependencies explicit •  Loose coupling •  Cannot instantiate a non-usable Object * Except: •  runtime dependencies •  objects with a shorter lifetime
  • 19. Instantiating Implementations So do all dependees need to provide fully initialized objects to their dependents when instantiating them? NO - THEY DO NOT INSTANTIATE THEM! Object Creation vs. Object Use •  ensures a loosely coupled system •  ensures testability
  • 20. Object Creation vs. Object Use creation Happens one time at the Composition Root. All Objects* are created/wired together at this time •  startup •  Request use Happens all the time throughout the application
  • 21. Object Creation What creates the objects? Forces specification of dependencies and their lifespan •  DIY DI •  wire.js / cujojs •  Inverted •  Intravenous •  AngularJS whole lotta others...
  • 22. Cross-Cutting Concerns What about the crap I might need? •  Logging •  Auditing •  Profiling •  Security •  Caching •  .... "Cross-cutting concerns"
  • 23. Cross-Cutting Concerns // DO NOT DO THIS: UserRepoRedis = function(...) { this.redis = ... this.logger = new Logger(); // or Logger.get() this.profiler = new Profiler(); // or Profiler.get() }; •  tightly coupled to Logger & Profiler implementations •  PITA to test
  • 24. Cross-Cutting Concerns // DO NOT DO THIS: UserRepoRedis = function(..., logger, profiler) { this.redis = ... this.logger = logger; this.profiler = profiler; }; Injection - so looks good BUT violating SRP!
  • 25. Cross-Cutting Concerns // DO NOT DO THIS: UserRepoRedis.prototype.save = function(user) { logger.log('Saving user: ' + user); profiler.startProfiling('saveUser'); ... do redis/actual save user stuff ... profiler.stopProfiling('saveUser'); logger.log('that took: ' + (start - end)); };
  • 26. Cross-Cutting Concerns •  Keep different functionality separate •  Single Responsibility Principle •  Interface Segregation Principle
  • 27. var LoggerFile = function(file) { // Implementation this.file = file; } LoggerFile.prototype = Object.create(Logger); LoggerFile.prototype.log = function(msg) { this.file.write(msg); }; Logging Interface and Implementation var Logger = { // Interface log: function(msg) {} , getLastLog: function() {} // get it out! };
  • 28. Mixin method - quick aside... // Composition not inheritance var MIXIN = function(base, extendme) { var prop; for (prop in base) { if (typeof base[prop] === 'function' && !extendme[prop]) { extendme[prop] = base[prop].bind(base); } } };
  • 29. Decorator var UserRepoLogger = function(repo, logger) { this.innerRepo = repo; this.logger = logger; MIXIN(repo, this); // Mixin repo's methods }; UserRepoLogger.prototype.save = function(user) { this.logger.log('Saving user: ' + user); return this.innerRepo.save(user); };
  • 30. Decorator // UserRepoLogger will Intercept save // all other methods will fall thru to // UserRepoRedis // This userRepo implements the UserRepo // interface therefore can be used anywhere var userRepo = new UserRepoLogger( new UserRepoRedis() , new LoggerFile() );
  • 31. var ProfilerTime = function() { this.profiles = {}; }; ProfilerTime.prototype = Object.create(Profiler); ProfilerTime.prototype.start = function(id) { this.profiles[id] = new Date().getTimestamp(); }; ProfilerTime.prototype.stop = function(id) { ... }; .... Profile Interface and Implementation var Profiler = { // Interface start: function(id) {} , stop: function(id) {} , getProfile: function(id) {} // get it out! };
  • 32. Decorator var UserRepoProfiler = function(repo, profiler) { this.innerRepo = repo; this.profiler = profiler; MIXIN(repo, this); // Mixin repo's methods }; UserRepoProfiler.prototype.save = f(user) { this.profiler.start('save user'); this.innerRepo.save(user); this.profiler.stop('save user');
  • 33. Intercept var redisRepo = new UserRepoRedis(); var profiler = new ProfilerTime(); var logger = new LoggerFile(); // Profiling UserRepo var userRepoProf = new UserRepoProfiler(redisRepo, profiler); // Logging Profiling UserRepo var userRepo = new UserRepoLogger(userRepoProf, logger);
  • 34. Testing Decorators Create the Decorator and Test the Interface: function testUserRepoLogger(repo) { var id = 99, user = { id: id, ... }, loggerMock = new LoggerMock(), testRepo = new UserRepoLogger(repo, loggerMock); testRepo.save(user); // verify loggerMock }
  • 35. Testing Decorators var repo = new UserRepoMock(); var logger = new LoggerMock(); var profiler = new ProfileMock(); var userRepo = new UserRepoProfile( new UserRepoLogger(repo, logger), profiler); // test UserRepo interface testRepo(userRepo); // verify logger and profiler mocks
  • 36. Decorator Pattern •  Constructor accepts 'inner' Object of the same type and any other necessary dependencies. •  Mixin with 'inner' Object to get default behavior for non-decorated methods. •  Decorate necessary interface methods and (optionally) delegate to 'inner' Object.
  • 37. var FindMatchesDistance = f(userRepo) { ... }; FindMatchesDistance.prototype = Object.create(FindMatches); var FindMatchesActivites = f(userRepo) { ... }; var FindMatchesLikes = f(userRepo) { ... }; Runtime/Short-lived Dependencies var FindMatches = { matches: function(userid) {} };
  • 38. Runtime/Short-lived Dependencies // User Controller needs a findMatches // implementation - but which one??? var UserController = function(findMatches, ...) { .... } Inject all three?? What if I make more? What if other classes need a dynamic findMatch implementation?
  • 39. var FindMatchFactoryImp = function(repo) { this.repo = repo; }; FindMatchFactoryImpl.prototype = Object.create(FindMatchFactory); Runtime/Short-lived Dependencies var FindMatchFactory = { getMatchImplementation: function(type) {} };
  • 40. Runtime/Short-lived Dependencies getMatchImplementation = function(type) { switch(type) { case 'likes': return new FindMatchesLikes(this.repo); break; .... default: return new FindMatchesActivites(this.repo); } };
  • 41. Runtime/Short-lived Dependencies var UserController = function(findMatchFactory) { this.findMatchFactory = findMatchFactory; }; UserController.prototype = Object.create(Controller); UserController.prototype.findMatches = function(type, uid) { var matcher = this.findMatchFactory.getMatchImplementation(type); return matcher.matches(uid); };
  • 42. Testing Abstract Factories var matchTypes = [ { name: 'likes', type: FindMatchesLikes } , .... ]; test(findMatchFactory) { matchTypes.forEach(function(type) { expect( findMatchFactory .getMatchImplementation(type.name) .toEqual(type.type); }); }
  • 43. Mocking Abstract Factories var TestFactoryImpl = function(expectedType, mockMatch) { this.expectedType = expectedType; this.mockMach = mockMatch; }; TestFactoryImpl.prototype = Object.create(FindMatchFactory); TestFactoryImpl.prototype.getMatchImplementation(type) { expect(type).toEqual(this.expectedType); return this.mockMatch; };
  • 44. Abstract Factory Pattern •  Abstract factory translates runtime parameter -> Dependency •  Factory implementation created at Composition Root along with everything else •  Inject factory implementation into the Constructor for runtime dependencies •  Object uses injected factory to get Dependency providing Runtime Value
  • 45. Testable JavaScript •  Composition not Inheritance (impl. lock-in) •  Program and Test to Interfaces / Interfaces are the API •  Create lots of small Single Responsibility Interfaces •  Decorate and Intercept Cross-Cutting Concerns •  Constructor Inject all Dependencies •  Inject Abstract Factory for run-time Dependencies