SlideShare uma empresa Scribd logo
1 de 51
Testable JavaScript
Architecting Your Application for
Testability
Mark Ethan Trostler
Google Inc.
twitter: @zzoass
mark@zzo.com
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/javascript-testing
Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
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!
};
Object Implements UserRepo
MIXIN
Object Implements
UserRepo
METHOD 1
METHOD 3
METHOD 2
METHOD 1
METHOD 4
METHOD 3
Mixin method
// 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
Testable JavaScript
Architecting Your Application for
Testability
Mark Ethan Trostler
Google Inc.
twitter: @zzoass
mark@zzo.com
Ensuring Testability
Watch the video with slide synchronization on
InfoQ.com!
http://www.infoq.com/presentations/javascript
-testing

Mais conteúdo relacionado

Destaque

Singkatan singkatan humor
Singkatan singkatan humorSingkatan singkatan humor
Singkatan singkatan humor0317315550
 
Narayaneeyam tamil transliteration 028
Narayaneeyam tamil transliteration 028Narayaneeyam tamil transliteration 028
Narayaneeyam tamil transliteration 028Ravi Ramakrishnan
 
Htsas Taipei Week1 Brian 20141007
Htsas Taipei Week1 Brian 20141007 Htsas Taipei Week1 Brian 20141007
Htsas Taipei Week1 Brian 20141007 ALPHA Camp Taiwan
 
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...Romy Huanuco
 
Practica N° 1Informe de fundamentos arimeticos y estadisticos
Practica N° 1Informe de fundamentos arimeticos y estadisticos Practica N° 1Informe de fundamentos arimeticos y estadisticos
Practica N° 1Informe de fundamentos arimeticos y estadisticos Alexis Ante
 
Ch 2 - Airport Mobile Internet - Introductions
Ch 2 - Airport Mobile Internet - IntroductionsCh 2 - Airport Mobile Internet - Introductions
Ch 2 - Airport Mobile Internet - IntroductionsLuis Martin-Domingo
 
πρόγραμμα εξεταστικής ιουνίου 2016
πρόγραμμα εξεταστικής ιουνίου 2016πρόγραμμα εξεταστικής ιουνίου 2016
πρόγραμμα εξεταστικής ιουνίου 2016Christos I. Papadakis
 
Plan your security
Plan your securityPlan your security
Plan your securityAccord Group
 
Moltiplichiamo...per gelosia!
Moltiplichiamo...per gelosia!Moltiplichiamo...per gelosia!
Moltiplichiamo...per gelosia!Lmariani
 
Plan de trabajo de comisión de brigadieres
Plan de trabajo de comisión de brigadieresPlan de trabajo de comisión de brigadieres
Plan de trabajo de comisión de brigadieresPinoCopy PinoCopy
 

Destaque (15)

ISO Neville
ISO NevilleISO Neville
ISO Neville
 
Singkatan singkatan humor
Singkatan singkatan humorSingkatan singkatan humor
Singkatan singkatan humor
 
Narayaneeyam tamil transliteration 028
Narayaneeyam tamil transliteration 028Narayaneeyam tamil transliteration 028
Narayaneeyam tamil transliteration 028
 
Htsas Taipei Week1 Brian 20141007
Htsas Taipei Week1 Brian 20141007 Htsas Taipei Week1 Brian 20141007
Htsas Taipei Week1 Brian 20141007
 
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...
Mensaje ambiental a los pueblos y gobiernos de j. d. perón ( una breve reflex...
 
Promoción de word
Promoción de wordPromoción de word
Promoción de word
 
Practica N° 1Informe de fundamentos arimeticos y estadisticos
Practica N° 1Informe de fundamentos arimeticos y estadisticos Practica N° 1Informe de fundamentos arimeticos y estadisticos
Practica N° 1Informe de fundamentos arimeticos y estadisticos
 
Matvejeva
MatvejevaMatvejeva
Matvejeva
 
Ch 2 - Airport Mobile Internet - Introductions
Ch 2 - Airport Mobile Internet - IntroductionsCh 2 - Airport Mobile Internet - Introductions
Ch 2 - Airport Mobile Internet - Introductions
 
πρόγραμμα εξεταστικής ιουνίου 2016
πρόγραμμα εξεταστικής ιουνίου 2016πρόγραμμα εξεταστικής ιουνίου 2016
πρόγραμμα εξεταστικής ιουνίου 2016
 
Plan your security
Plan your securityPlan your security
Plan your security
 
Moltiplichiamo...per gelosia!
Moltiplichiamo...per gelosia!Moltiplichiamo...per gelosia!
Moltiplichiamo...per gelosia!
 
різноманітність тварин у природі.
 різноманітність тварин у природі.  різноманітність тварин у природі.
різноманітність тварин у природі.
 
Finding our voices
Finding our voicesFinding our voices
Finding our voices
 
Plan de trabajo de comisión de brigadieres
Plan de trabajo de comisión de brigadieresPlan de trabajo de comisión de brigadieres
Plan de trabajo de comisión de brigadieres
 

Mais de C4Media

Streaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live VideoStreaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live VideoC4Media
 
Next Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy MobileNext Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy MobileC4Media
 
Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020C4Media
 
Understand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java ApplicationsUnderstand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java ApplicationsC4Media
 
Kafka Needs No Keeper
Kafka Needs No KeeperKafka Needs No Keeper
Kafka Needs No KeeperC4Media
 
High Performing Teams Act Like Owners
High Performing Teams Act Like OwnersHigh Performing Teams Act Like Owners
High Performing Teams Act Like OwnersC4Media
 
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to JavaDoes Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to JavaC4Media
 
Service Meshes- The Ultimate Guide
Service Meshes- The Ultimate GuideService Meshes- The Ultimate Guide
Service Meshes- The Ultimate GuideC4Media
 
Shifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CDShifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CDC4Media
 
CI/CD for Machine Learning
CI/CD for Machine LearningCI/CD for Machine Learning
CI/CD for Machine LearningC4Media
 
Fault Tolerance at Speed
Fault Tolerance at SpeedFault Tolerance at Speed
Fault Tolerance at SpeedC4Media
 
Architectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep SystemsArchitectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep SystemsC4Media
 
ML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.jsML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.jsC4Media
 
Build Your Own WebAssembly Compiler
Build Your Own WebAssembly CompilerBuild Your Own WebAssembly Compiler
Build Your Own WebAssembly CompilerC4Media
 
User & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix ScaleUser & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix ScaleC4Media
 
Scaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's EdgeScaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's EdgeC4Media
 
Make Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home EverywhereMake Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home EverywhereC4Media
 
The Talk You've Been Await-ing For
The Talk You've Been Await-ing ForThe Talk You've Been Await-ing For
The Talk You've Been Await-ing ForC4Media
 
Future of Data Engineering
Future of Data EngineeringFuture of Data Engineering
Future of Data EngineeringC4Media
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreAutomated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreC4Media
 

Mais de C4Media (20)

Streaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live VideoStreaming a Million Likes/Second: Real-Time Interactions on Live Video
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
 
Next Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy MobileNext Generation Client APIs in Envoy Mobile
Next Generation Client APIs in Envoy Mobile
 
Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020Software Teams and Teamwork Trends Report Q1 2020
Software Teams and Teamwork Trends Report Q1 2020
 
Understand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java ApplicationsUnderstand the Trade-offs Using Compilers for Java Applications
Understand the Trade-offs Using Compilers for Java Applications
 
Kafka Needs No Keeper
Kafka Needs No KeeperKafka Needs No Keeper
Kafka Needs No Keeper
 
High Performing Teams Act Like Owners
High Performing Teams Act Like OwnersHigh Performing Teams Act Like Owners
High Performing Teams Act Like Owners
 
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to JavaDoes Java Need Inline Types? What Project Valhalla Can Bring to Java
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
 
Service Meshes- The Ultimate Guide
Service Meshes- The Ultimate GuideService Meshes- The Ultimate Guide
Service Meshes- The Ultimate Guide
 
Shifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CDShifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CD
 
CI/CD for Machine Learning
CI/CD for Machine LearningCI/CD for Machine Learning
CI/CD for Machine Learning
 
Fault Tolerance at Speed
Fault Tolerance at SpeedFault Tolerance at Speed
Fault Tolerance at Speed
 
Architectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep SystemsArchitectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep Systems
 
ML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.jsML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.js
 
Build Your Own WebAssembly Compiler
Build Your Own WebAssembly CompilerBuild Your Own WebAssembly Compiler
Build Your Own WebAssembly Compiler
 
User & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix ScaleUser & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix Scale
 
Scaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's EdgeScaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's Edge
 
Make Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home EverywhereMake Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home Everywhere
 
The Talk You've Been Await-ing For
The Talk You've Been Await-ing ForThe Talk You've Been Await-ing For
The Talk You've Been Await-ing For
 
Future of Data Engineering
Future of Data EngineeringFuture of Data Engineering
Future of Data Engineering
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreAutomated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
 

Último

[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
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
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
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
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
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
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
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
 

Último (20)

[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 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
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
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
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
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
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
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
 

Testable JavaScriptUntitled

  • 1. Testable JavaScript Architecting Your Application for Testability Mark Ethan Trostler Google Inc. twitter: @zzoass mark@zzo.com
  • 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /javascript-testing
  • 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  • 5.
  • 7. Interfaces not Implementation var UserRepository = { get: function(id) {} , save: function(user) {} , getAll: function() {} , edit: function(id, user) {} , delete: function(id) {} , query: function(query) {} };
  • 8. Interfaces not Implementation function test(repo) { var id = 99, user = { id: id, ... }; repo.save(user); expect(repo.get(id)).toEqual(user); } Test the interface
  • 9. Interfaces not Implementation var UserRepoRedis = function(host, port, opt) { this.redis = redis.createClient({ ... }); }; UserRepoRedis.prototype = Object.create(UserRepo); UserRepoRedis.prototype.save = function(user) { ... };
  • 10. Interfaces not Implementation ● Object is interface - no initialization ● Implementation has constructor with injected dependencies ● Prototype is Object.create(Interface) ● Override with prototype functions
  • 11. 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
  • 12. Interfaces not Implementation var UserRepoS3 = function(key, secret, bucket) { this.s3 = knox.createClient({...}); }; UserRepoS3.prototype = Object.create(UserRepo); UserRepoS3.prototype.save = function(user) { ... }
  • 13. 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
  • 14. Single Responsibility Principle Every interface should have a single responsibility, and that responsibility should be entirely encapsulated by the interface. Responsibility = Reason To Change
  • 15. Interface Segregation Principle No object should be forced to depend on methods it does not use.
  • 16. Interface Pattern ● Single Responsibility Principle ● Match Sets with Gets ● More Smaller / Fewer Bigger ● Interface Segregation Principle ● Test and Program to Interface Only
  • 17. Using Interfaces You've created nice interfaces - use them wisely! // DO NOT DO THIS! var UserController = function() { this.userRepo = new UserRepoRedis(); };
  • 18. Using Interfaces You've created nice interfaces - use them wisely! // DO THIS - Inject the dependencies! var UserController = function(userRepo) { this.userRepo = userRepo; };
  • 19. Liskov Substitution Principle Any objects that implement an interface can be used interchangeably
  • 20. 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
  • 21. 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
  • 22. 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
  • 23. Object Creation What creates the objects? Forces specification of dependencies and their lifespan ● DIY DI ● wire.js / cujojs ● Inverted ● Intravenous ● AngularJS whole lotta others...
  • 24. Cross-Cutting Concerns What about the crap I might need? ● Logging ● Auditing ● Profiling ● Security ● Caching ● .... "Cross-cutting concerns"
  • 25. 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
  • 26. 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!
  • 27. 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)); };
  • 28. Cross-Cutting Concerns ● Keep different functionality separate ● Single Responsibility Principle ● Interface Segregation Principle
  • 29. 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! };
  • 30. Object Implements UserRepo MIXIN Object Implements UserRepo METHOD 1 METHOD 3 METHOD 2 METHOD 1 METHOD 4 METHOD 3
  • 31. Mixin method // 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); } } };
  • 32. 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); };
  • 33. 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() );
  • 34. 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! };
  • 35. 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'); };
  • 36. 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);
  • 37. 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 }
  • 38. 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
  • 39. 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.
  • 40. 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) {} };
  • 41. 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?
  • 42. var FindMatchFactoryImp = function(repo) { this.repo = repo; }; FindMatchFactoryImpl.prototype = Object.create(FindMatchFactory); Runtime/Short-lived Dependencies var FindMatchFactory = { getMatchImplementation: function(type) {} };
  • 43. Runtime/Short-lived Dependencies getMatchImplementation = function(type) { switch(type) { case 'likes': return new FindMatchesLikes(this.repo); break; .... default: return new FindMatchesActivites(this.repo); } };
  • 44. 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); };
  • 45. Testing Abstract Factories var matchTypes = [ { name: 'likes', type: FindMatchesLikes } , .... ]; test(findMatchFactory) { matchTypes.forEach(function(type) { expect( findMatchFactory .getMatchImplementation(type.name) .toEqual(type.type); }); }
  • 46. 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; };
  • 47. 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
  • 48. 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
  • 49. Testable JavaScript Architecting Your Application for Testability Mark Ethan Trostler Google Inc. twitter: @zzoass mark@zzo.com
  • 51. Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations/javascript -testing