SlideShare una empresa de Scribd logo
1 de 38
Descargar para leer sin conexión
How to test complex SaaS solutions! 
! 
The Family! 
16th july 2014
Founder & CTO! 
! 
@guillaumepotier! 
http://imctobitch.com
By the numbers 
• 4 years old! 
• 23 employes and counting! 
- ~ 10 tech / product! 
- ~ 10 sales! 
- ~ 3 support, market, administrative ! 
• 400+ clients! 
- Total, Orange, Areva, Air Liquide, Google, SNCF, AXA, Société 
Générale, Sanofi, L’Oréal, Crédit Agricole, Danone, Deloitte, Capgemini, 
Auchan…! 
• 4000+ tests! 
- UT backend, functional backend! 
- UT javascript, functional javascript! 
! 
! 
!
What we’ll talk about 
• Why testing?! 
• What need to be tested?! 
• How to test?! 
• Tools! 
• Limits..! 
• Going further! 
!
Why testing? 
Sometimes shit may happen
Why testing? 
Tests are here to prevent that (at least they try..)
Why testing? 
Tests also allow you refactor without fearing to break things
Why testing? 
Tests are great to rely on other people code (and other people on yours)
Why testing? 
Ultimately, tests allow you to be faster!
Why testing? 
A little more seriously..! 
Let’s see a real Parsley.js example here
Why testing?
Why testing? 
• 10+ classes! 
• 2k+ lines! 
• ~200 tests (UT & functional)! 
• ran in < 3sec in browser! 
• ran in < 1sec in CLI! 
! 
! 
http://parsleyjs.org/doc/tests.html
Why testing? 
max: function (value) { 
return $.extend(new Validator.Assert().LessThanOrEqual(value), { priority: 30 }); 
UT : validate your methods API and behavior 
! 
} 
it('should have a max validator', function () { 
expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); 
expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); 
}); 
✓ should have a max validator 
Code 
Test 
Result
Why testing? 
max: function (value) { 
return $.extend(new Validator.Assert().LessThan(value), { priority: 30 }); 
Prevent regressions, ensure 3rd party libs consistency 
! 
} 
it('should have a max validator', function () { 
expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); 
expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); 
}); 
1) should have a max validator 
Code 
Test 
Result
Why testing?
Why testing?
Why testing? 
Fixes bugs found to ensure they’ll never show up again 
! 
it('should have a max validator', function () { 
expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); 
expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); 
expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); 
expect(parsleyValidator.validate('5', parsleyValidator.max(”10”))).to.be(true); 
}); 
✓ should have a max validator 
Code 
Test 
Result 
max: function (value) { 
return $.extend(new Validator.Assert().LessThan(value), { 
priority: 30, 
requirementsTransformer: function () { 
return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; 
} 
}); 
}
Why testing? 
it('should show custom error message with variabilized parameters', function () { 
$('body').append('<input type="text" id="element" value="bar" data-parsley-minlength="7" data-parsley-minlength- 
message="foo %s bar"/>'); 
var parsleyField = $('#element').psly(); 
parsleyField.validate(); 
! 
expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('foo 7 bar'); 
}); 
Functional test : validate your end-user behavior
Why testing? 
it('should save some calls for querries already done', function (done) { 
$('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" required 
name="element" value="foo" />'); 
var parsleyInstance = $('#element').parsley(); 
! 
sinon.stub($, 'ajax').returns($.Deferred().resolve({}, 'success', { status: 200, state: function () { return 
'resolved' } })); 
parsleyInstance.asyncIsValid() 
.done(function () { 
expect($.ajax.calledOnce).to.be(true); 
expect($.ajax.calledWithMatch({ data: { "element": "foo" } })).to.be(true); 
$.ajax.restore(); 
sinon.stub($, 'ajax').returns($.Deferred().reject({ status: 400, state: function () { return 'rejected' } 
}, 'error', 'error')); 
! 
$('#element').val('bar'); 
parsleyInstance.asyncIsValid() 
.fail(function () { 
expect($.ajax.calledOnce).to.be(true); 
expect($.ajax.calledWithMatch({ data: { "element": "bar" } })).to.be(true); 
! 
$.ajax.restore(); 
sinon.stub($, 'ajax').returns($.Deferred().resolve({}, 'success', { status: 200, state: function () { 
return 'resolved' } })); 
$('#element').val('foo'); 
! 
parsleyInstance.asyncIsValid() 
.done(function () { 
expect($.ajax.callCount).to.be(0); 
expect($.ajax.calledOnce).to.be(false); 
$.ajax.restore(); 
done(); 
}); 
}); 
}); 
});
What need to be tested?
What need to be tested? 
• Application logic (services, algorithms, microapps)! 
• API responses! 
• Application behavior, End-user responses! 
! 
TO 
BE 
TESTED
What need to be tested?
What need to be tested? 
• getters / setters! 
• already tested 3rd party libraries! 
• ORM, all exceptions, errors! 
! 
NOT 
TO 
BE 
TESTED
What need to be tested?
What need to be tested? 
100% 
coverage!
What need to be tested?
What need to be tested?
What need to be tested?
How to test?
How to test?
How to test? 
class SynchronizedUserTest extends PHPUnit_Framework_TestCase 
{ 
/** 
* @covers ::construct() 
* @expectedException InvalidArgumentException 
* @expectedExceptionMessage User and IntercomUser are not the same. 
*/ 
public function testConstructWithWrongEmails() 
{ 
$intercomUser = new IntercomUser(1, 'bar@foo.fr'); 
$user = (new User)->setEmail('foo@bar.fr'); 
$this->setProperty($user, 'id', 1); 
! 
new SynchronizedUser($intercomUser, $user); 
} 
! 
/** 
* @covers ::construct() 
* @expectedException InvalidArgumentException 
* @expectedExceptionMessage User and IntercomUser are not the same. 
*/ 
public function testConstructWithWrongIds() 
{ 
$intercomUser = new IntercomUser(2, 'foo@bar.fr'); 
$user = (new User)->setEmail('foo@bar.fr'); 
$this->setProperty($user, 'id', 1); 
! 
new SynchronizedUser($intercomUser, $user); 
} 
}
How to test? 
class SynchronizedUser 
{ 
private $intercomUser; 
private $user; ! 
/** 
* @throws InvalidArgumentException If the user intercom and user wisembly doesn't match 
*/ 
public function __construct(IntercomUser $intercomUser, User $user) 
{ 
if ($intercomUser->getUserId() !== $user->getId() || $intercomUser->getEmail() !== mb_strtolower($user->getEmail(), 
mb_detect_encoding($user->getEmail()))) { 
throw new InvalidArgumentException('User and IntercomUser are not the same.'); 
} ! 
$this->intercomUser = $intercomUser; 
$this->user = $user; 
} ! 
/** 
* @return IntercomUser 
*/ 
public function getIntercomUser() 
{ 
return $this->intercomUser; 
} ! 
/** 
* @return User 
*/ 
public function getUser() 
{ 
return $this->user; 
} 
}
How to test?
How to test?
Tools 
• PHPUnit! 
• Behat, PHPSpec..! 
• Mocha, Jasmine, QUnit..! 
• Karma, PhantomJS, TestEM..! 
• Travis, Jenkins, Shipper, CodeShip..! 
!
Limits 
• CSS glitches! 
• User experience! 
• Browser compatibility! 
• Do not take to much time to code / maintain tests!! 
• Do not have too long test suites!! 
• Fixtures for functional tests! 
• Isolate tests among themselves!! 
!
Going further 
• Script & test deploy scripts..! 
• SLA & performance tests..! 
!
How to test complex SaaS applications - The family july 2014

Más contenido relacionado

La actualidad más candente

Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::Manager
Jay Shirley
 
Instant Dynamic Forms with #states
Instant Dynamic Forms with #statesInstant Dynamic Forms with #states
Instant Dynamic Forms with #states
Konstantin Käfer
 

La actualidad más candente (20)

04 Advanced Javascript
04 Advanced Javascript04 Advanced Javascript
04 Advanced Javascript
 
async/await Revisited
async/await Revisitedasync/await Revisited
async/await Revisited
 
Building Better Applications with Data::Manager
Building Better Applications with Data::ManagerBuilding Better Applications with Data::Manager
Building Better Applications with Data::Manager
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011
 
QA for PHP projects
QA for PHP projectsQA for PHP projects
QA for PHP projects
 
November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2November Camp - Spec BDD with PHPSpec 2
November Camp - Spec BDD with PHPSpec 2
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Functional Structures in PHP
Functional Structures in PHPFunctional Structures in PHP
Functional Structures in PHP
 
PHPSpec BDD for PHP
PHPSpec BDD for PHPPHPSpec BDD for PHP
PHPSpec BDD for PHP
 
My Development Story
My Development StoryMy Development Story
My Development Story
 
Backbone - TDC 2011 Floripa
Backbone - TDC 2011 FloripaBackbone - TDC 2011 Floripa
Backbone - TDC 2011 Floripa
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Proposed PHP function: is_literal()
Proposed PHP function: is_literal()Proposed PHP function: is_literal()
Proposed PHP function: is_literal()
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with Jasmine
 
Espresso devoxx 2014
Espresso devoxx 2014Espresso devoxx 2014
Espresso devoxx 2014
 
Instant Dynamic Forms with #states
Instant Dynamic Forms with #statesInstant Dynamic Forms with #states
Instant Dynamic Forms with #states
 
Get into the FLOW with Extbase
Get into the FLOW with ExtbaseGet into the FLOW with Extbase
Get into the FLOW with Extbase
 
Perl 6 by example
Perl 6 by examplePerl 6 by example
Perl 6 by example
 

Similar a How to test complex SaaS applications - The family july 2014

Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
Michelangelo van Dam
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
mfrost503
 
JavaScript Sprachraum
JavaScript SprachraumJavaScript Sprachraum
JavaScript Sprachraum
patricklee
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
wpnepal
 

Similar a How to test complex SaaS applications - The family july 2014 (20)

Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
 
PHP Unit Testing
PHP Unit TestingPHP Unit Testing
PHP Unit Testing
 
WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
JQuery Presentation
JQuery PresentationJQuery Presentation
JQuery Presentation
 
JavaScript Promise
JavaScript PromiseJavaScript Promise
JavaScript Promise
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.js
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
 
From typing the test to testing the type
From typing the test to testing the typeFrom typing the test to testing the type
From typing the test to testing the type
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Google guava
Google guavaGoogle guava
Google guava
 
JavaScript Sprachraum
JavaScript SprachraumJavaScript Sprachraum
JavaScript Sprachraum
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
Angular Tutorial Freshers and Experienced
Angular Tutorial Freshers and ExperiencedAngular Tutorial Freshers and Experienced
Angular Tutorial Freshers and Experienced
 

Último

Último (20)

Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
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?
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
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)
 

How to test complex SaaS applications - The family july 2014

  • 1. How to test complex SaaS solutions! ! The Family! 16th july 2014
  • 2. Founder & CTO! ! @guillaumepotier! http://imctobitch.com
  • 3. By the numbers • 4 years old! • 23 employes and counting! - ~ 10 tech / product! - ~ 10 sales! - ~ 3 support, market, administrative ! • 400+ clients! - Total, Orange, Areva, Air Liquide, Google, SNCF, AXA, Société Générale, Sanofi, L’Oréal, Crédit Agricole, Danone, Deloitte, Capgemini, Auchan…! • 4000+ tests! - UT backend, functional backend! - UT javascript, functional javascript! ! ! !
  • 4. What we’ll talk about • Why testing?! • What need to be tested?! • How to test?! • Tools! • Limits..! • Going further! !
  • 5. Why testing? Sometimes shit may happen
  • 6. Why testing? Tests are here to prevent that (at least they try..)
  • 7. Why testing? Tests also allow you refactor without fearing to break things
  • 8. Why testing? Tests are great to rely on other people code (and other people on yours)
  • 9. Why testing? Ultimately, tests allow you to be faster!
  • 10. Why testing? A little more seriously..! Let’s see a real Parsley.js example here
  • 12. Why testing? • 10+ classes! • 2k+ lines! • ~200 tests (UT & functional)! • ran in < 3sec in browser! • ran in < 1sec in CLI! ! ! http://parsleyjs.org/doc/tests.html
  • 13. Why testing? max: function (value) { return $.extend(new Validator.Assert().LessThanOrEqual(value), { priority: 30 }); UT : validate your methods API and behavior ! } it('should have a max validator', function () { expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); }); ✓ should have a max validator Code Test Result
  • 14. Why testing? max: function (value) { return $.extend(new Validator.Assert().LessThan(value), { priority: 30 }); Prevent regressions, ensure 3rd party libs consistency ! } it('should have a max validator', function () { expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); }); 1) should have a max validator Code Test Result
  • 17. Why testing? Fixes bugs found to ensure they’ll never show up again ! it('should have a max validator', function () { expect(parsleyValidator.validate('foo', parsleyValidator.max(10))).to.be(false); expect(parsleyValidator.validate('5', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('10', parsleyValidator.max(10))).to.be(true); expect(parsleyValidator.validate('42', parsleyValidator.max(10))).to.be(false); expect(parsleyValidator.validate('5', parsleyValidator.max(”10”))).to.be(true); }); ✓ should have a max validator Code Test Result max: function (value) { return $.extend(new Validator.Assert().LessThan(value), { priority: 30, requirementsTransformer: function () { return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; } }); }
  • 18. Why testing? it('should show custom error message with variabilized parameters', function () { $('body').append('<input type="text" id="element" value="bar" data-parsley-minlength="7" data-parsley-minlength- message="foo %s bar"/>'); var parsleyField = $('#element').psly(); parsleyField.validate(); ! expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('foo 7 bar'); }); Functional test : validate your end-user behavior
  • 19. Why testing? it('should save some calls for querries already done', function (done) { $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" required name="element" value="foo" />'); var parsleyInstance = $('#element').parsley(); ! sinon.stub($, 'ajax').returns($.Deferred().resolve({}, 'success', { status: 200, state: function () { return 'resolved' } })); parsleyInstance.asyncIsValid() .done(function () { expect($.ajax.calledOnce).to.be(true); expect($.ajax.calledWithMatch({ data: { "element": "foo" } })).to.be(true); $.ajax.restore(); sinon.stub($, 'ajax').returns($.Deferred().reject({ status: 400, state: function () { return 'rejected' } }, 'error', 'error')); ! $('#element').val('bar'); parsleyInstance.asyncIsValid() .fail(function () { expect($.ajax.calledOnce).to.be(true); expect($.ajax.calledWithMatch({ data: { "element": "bar" } })).to.be(true); ! $.ajax.restore(); sinon.stub($, 'ajax').returns($.Deferred().resolve({}, 'success', { status: 200, state: function () { return 'resolved' } })); $('#element').val('foo'); ! parsleyInstance.asyncIsValid() .done(function () { expect($.ajax.callCount).to.be(0); expect($.ajax.calledOnce).to.be(false); $.ajax.restore(); done(); }); }); }); });
  • 20. What need to be tested?
  • 21. What need to be tested? • Application logic (services, algorithms, microapps)! • API responses! • Application behavior, End-user responses! ! TO BE TESTED
  • 22. What need to be tested?
  • 23. What need to be tested? • getters / setters! • already tested 3rd party libraries! • ORM, all exceptions, errors! ! NOT TO BE TESTED
  • 24. What need to be tested?
  • 25. What need to be tested? 100% coverage!
  • 26. What need to be tested?
  • 27. What need to be tested?
  • 28. What need to be tested?
  • 31. How to test? class SynchronizedUserTest extends PHPUnit_Framework_TestCase { /** * @covers ::construct() * @expectedException InvalidArgumentException * @expectedExceptionMessage User and IntercomUser are not the same. */ public function testConstructWithWrongEmails() { $intercomUser = new IntercomUser(1, 'bar@foo.fr'); $user = (new User)->setEmail('foo@bar.fr'); $this->setProperty($user, 'id', 1); ! new SynchronizedUser($intercomUser, $user); } ! /** * @covers ::construct() * @expectedException InvalidArgumentException * @expectedExceptionMessage User and IntercomUser are not the same. */ public function testConstructWithWrongIds() { $intercomUser = new IntercomUser(2, 'foo@bar.fr'); $user = (new User)->setEmail('foo@bar.fr'); $this->setProperty($user, 'id', 1); ! new SynchronizedUser($intercomUser, $user); } }
  • 32. How to test? class SynchronizedUser { private $intercomUser; private $user; ! /** * @throws InvalidArgumentException If the user intercom and user wisembly doesn't match */ public function __construct(IntercomUser $intercomUser, User $user) { if ($intercomUser->getUserId() !== $user->getId() || $intercomUser->getEmail() !== mb_strtolower($user->getEmail(), mb_detect_encoding($user->getEmail()))) { throw new InvalidArgumentException('User and IntercomUser are not the same.'); } ! $this->intercomUser = $intercomUser; $this->user = $user; } ! /** * @return IntercomUser */ public function getIntercomUser() { return $this->intercomUser; } ! /** * @return User */ public function getUser() { return $this->user; } }
  • 35. Tools • PHPUnit! • Behat, PHPSpec..! • Mocha, Jasmine, QUnit..! • Karma, PhantomJS, TestEM..! • Travis, Jenkins, Shipper, CodeShip..! !
  • 36. Limits • CSS glitches! • User experience! • Browser compatibility! • Do not take to much time to code / maintain tests!! • Do not have too long test suites!! • Fixtures for functional tests! • Isolate tests among themselves!! !
  • 37. Going further • Script & test deploy scripts..! • SLA & performance tests..! !