Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Phantom, Zombie & Karma, overview of the greatest testing tools for modern web app

Phantom, Zombie & Karma, overview of the greatest testing tools for modern web app

Presentation by Pierre Gayvallet and Jean-Laurent de Morlhon, given @ Devoxx 2013

Jean-Laurent de Morlhon

November 20, 2013
Tweet

More Decks by Jean-Laurent de Morlhon

Other Decks in Programming

Transcript

  1. @morlhon / @ wayofspark #DV13 - #jswebtesting Phantom, Zombie &

    Karma, overview of the greatest testing tools for modern web app. Jean-Laurent de Morlhon & Pierre Gayvalet
  2. @morlhon / @wayofspark #DV13-#jswebtesting Freelance developer 
 7 years of

    web development" 3 years of « full front stack » " ! @wayofspark" ! [email protected]" Pierre Gayvallet
  3. @morlhon / @wayofspark #DV13-#jswebtesting • QUnit • Sinon.js • KarmaJS

    • Mocha • Chai.js • Buster.js • jsCover • Plato • Wrap-up Agenda • A word about testing • Test taxonomy • Methodology • vocabulary • Phantom.js • Zombie.js LIVE LIVE LIVE LIVE LIVE LIVE
  4. @morlhon / @wayofspark #DV13-#jswebtesting Unit tests TEST • Technical test

    • Ultra fast (- 10 ms) • Class is tested in isolation of the others.
  5. @morlhon / @wayofspark #DV13-#jswebtesting Unit tests @Test public void should_find_winning_bids_when_there_is_option_bids()

    { Bid bid = new Bid(); bid.setStatus(OPTION); ! ExpiredBids expiredBids = new ExpiredBids(); expiredBids.add(bid); ! assertThat(expiredBids.isAnyWinning()).isTrue(); }
  6. @morlhon / @wayofspark #DV13-#jswebtesting Integration tests • Technical test •

    Fast (500ms to 1-2s) • Code is tested in logical group, in slice like in a carpacio. TEST
  7. @morlhon / @wayofspark #DV13-#jswebtesting Integration test example @Test public void

    i_can_retrieve_auctions_bided_by_an_investor() { Investor investor = investors.add(buildInvestor()); Auction auction = auctions.add(buildAuction()); ! auctions.addBidToAuction(auction.getId(), buildBid()); ! assertThat(auctions.fromInvestors(investor.get_id())).isNotEmpty(); }
  8. @morlhon / @wayofspark #DV13-#jswebtesting Acceptance tests TEST • Functional test

    • slooOOOoow +500ms • Application is tested from one end to the other
  9. @morlhon / @wayofspark #DV13-#jswebtesting Acceptance test example Scenario: substract two

    numbers Given I have entered 10 in the calculator And I have entered 7 in the calculator When I press substract Then the result should be 3 on the screen BDD Style (Gherkin)
  10. @morlhon / @wayofspark #DV13-#jswebtesting Acceptance Test example it 'should find

    my company account from siren', (done) -> browser = new Browser() browser.visit home, -> browser.clickLink 'Register', -> browser.choose ‘A company' browser.pressButton 'Sent', -> browser.fill 'companyCode', '394149496' browser.clickLink 'lookup', -> expect(browser.evaluate('$(\'input[name="name"]\').val()')).to.equal '42CRAFT' done()
  11. @morlhon / @wayofspark #DV13-#jswebtesting Outside In Techniques Write a failing

    acceptance tests Write a failing unit test Make the unit test pass, simply Refactor
  12. @morlhon / @wayofspark #DV13-#jswebtesting Headless Browser A browser « with

    no head ? » A browser with no graphical Interface ! Executable from the command line Mega-Ultra-Fast Can be automated We can’t always know what’s going on
  13. @morlhon / @wayofspark #DV13-#jswebtesting Headless Browser •PhantomJS ( qt )

    •HtmlUnit ( java ) •Zombie.js ( node.js ) •...
  14. @morlhon / @wayofspark #DV13-#jswebtesting PhantomJS Full Web Stack No Browser

    Required http://phantomjs.org/ http://github.com/ariya/phantomjs
  15. @morlhon / @wayofspark #DV13-#jswebtesting • Headless browser based on QtWebKit

    (JavascriptCore) - full javascript API - true rendering (no emulation) - pure headless (no X11 required) ! • Created by Ariya Hidayat (@ariyahidayat) in 2010 ! • Cross platform (Linux, MacOSX and Window) ! • Blazing fast • Used in test workflow of many famous projects (Bootstrap, Grunt...) PhantomJS
  16. @morlhon / @wayofspark #DV13-#jswebtesting var page = require('webpage').create(); ! page.open('http://localhost:8080',

    function (status) { ! var title = page.evaluate(function() { return document.title; }); console.log("page title = " + title); ! var titles = page.evaluate(function() { var productTitleElements = document.querySelectorAll(".produit h2"); var productTitles = []; for(var i=0; i<productTitleElements.length; i++) { productTitles.push(productTitleElements[i].textContent); } return productTitles; }); console.log("article titles = ", JSON.stringify(titles)); ! phantom.exit(); }); Code Example - PhantomJS
  17. @morlhon / @wayofspark #DV13-#jswebtesting ! // BAD var title =

    null; page.evaluate(function() { title = document.title ; }); console.log(title) // prints null. ! ! ! // GOOD var title = page.evaluate(function() { return document.title; }); console.log(title) // prints the title. ! • Dual evaluation context (browser and server) ! • Communication between contexts can be tricky (only primitive types) ! • Easy to use it wrong at first Evaluation context
  18. @morlhon / @wayofspark #DV13-#jswebtesting Features • Embed Coffeescript compiler !

    • Handles browser viewport ! • Allows screenshots ! • Can use the chrome remote debugger ! • Webkit engine : very reliable with web standards : - Canvas, LocalStorage, Css Animations, WebWorkers...
  19. @morlhon / @wayofspark #DV13-#jswebtesting Rendering limitations • Flash - Removed

    since version 1.5 (was preventing phantom to be pure headless) • CSS 3D • Video and Audio • WebGL • Geolocation (but can easily be mocked) • Fonts (rarely a problem when used for testing) ! Yep, everything else is working !
  20. @morlhon / @wayofspark #DV13-#jswebtesting Ecosystem - GhostDriver (implements WebDriver spec)

    ! - Test Runners - Poltergeist (capibara) - mocha-phantom-js - ... ! - Test Frameworks - Lotte - WebSpecter - CasperJs - ... ! - Screenshot tools (capturejs, node-webshot, …)
  21. @morlhon / @wayofspark #DV13-#jswebtesting In a few words... Very powerful

    tool : true rendering, reliable, very fast. ! But... – Raw API which can be hard to handle – The dual execution context can be confusing at first – Tricky asynchronous nature (callback cascading) – Actually only a browser, not a test framework ! This is why...
  22. @morlhon / @wayofspark #DV13-#jswebtesting CasperJS navigation scripting & testing utility

    for PhantomJS http://casperjs.org http://github.com/n1k0/casperjs
  23. @morlhon / @wayofspark #DV13-#jswebtesting CasperJS • Based on PhantomJS !

    • Adds an higher level API : • Fluent API • More graceful handling of asynchronism nature • Make is easier to interact with the page • Allow the creation of functional test suites • A lot of sugar methods
  24. @morlhon / @wayofspark #DV13-#jswebtesting var casper = require("casper").create(); ! casper.on("load.finished",

    function(status) { if(status=="fail") { this.exit(); } }); ! casper.start(url1); casper.thenOpen(url2); casper.thenOpen(url3, function() { ! // DO SOMETHING ! }); var page = require("webpage").create(); ! page.open(url1, function(status) { if(status=="fail") { phantom.exit(); } ! page.open(url2, function(status) { if(status=="fail") { phantom.exit(); } page.open(url3, function(status) { if(status=="fail") { phantom.exit(); } ! // DO SOMETHING ! }); }); }); Navigation steps Opening successive urls… Using Phantom Using Casper - Increase lisibility - Factorize handlers - Avoid the « Callback Pyramid Effect »
  25. @morlhon / @wayofspark #DV13-#jswebtesting var page = require('webpage').create(); page.open("http://localhost:8080", function(st)

    { if(st != "success") { phantom.exit(); } var headerClipRect = page.evaluate(function() { var clip = document.querySelector('#header').getBoundingClientRect(); return { top: clip.top, left: clip.left, width: clip.width, height: clip.height }; }); var oldClipRect = page.clipRect; page.clipRect = headerClipRect; page.render("header.png"); page.clipRect = oldClipRect; phantom.exit(); }); Easier Making a screenshot of a part of the viewport… Using Phantom Using Casper var casper = require("casper").create() casper.start('http://localhost:8080', function() { this.captureSelector('header.png', '#header'); }); ! casper.run();
  26. @morlhon / @wayofspark #DV13-#jswebtesting More sugar ? Enhance the PhantomJS

    API in many ways : ! - page manipulation ( click, fill, mouseEvent… ) ! - synchronization ( waitForSelector, waitForResource, ... ) ! - accessing DOM ( getElementAttribute, getFormValues, …) ! - debug methods ( debugHTML, dump, … )
  27. @morlhon / @wayofspark #DV13-#jswebtesting Tester API casper.test.comment('home tests'); ! casper.start(homeUrl,

    function() { this.test.assertTitle(homeTitle, "home title should match"); ! this.test.assertEval(function() { return $(".product").length == 7; }, "home should display 7 products"); ! this.click(".product a").click(); }); ! casper.then(function() { this.test.assertUrlMatch(/^http:\/\/localhost:8080\/product/, "should be on a product page"); }); ! casper.run(function() { this.test.done(3); });
  28. @morlhon / @wayofspark #DV13-#jswebtesting Launching test suites - setup and

    teardown as CLI arguments and global to every test... :/ ! - export results in xUnit format (for CI integration) $ casperjs test --includes=foo.js,bar.js \ ## these scripts will be included at startup --pre=pre-test.js \ ## this will be executed before each test --post=post-test.js \ ## this will be executed after each test --direct \ ## log messages redirected to the console --log-level=debug \ ## log level --fail-fast \ ## stop suite after first fail. --xunit=xunit.xml ## path for the xunit output file test1.js test2.js /path/to/another/test/dir ## path to test and test folders to launch
  29. @morlhon / @wayofspark #DV13-#jswebtesting Version 1.1 (still in beta) Test

    syntax closer to xUnit format casper.test.begin('My suite name', 2, { setUp: function(test) { // some setup stuff }, ! tearDown: function(test) { // some tearDown stuff }, ! test: function(test) { casper.open(url, function() { test.assertTitle("my title", "title should match") }); casper.then(function() { // some more test }); casper.then(function() { test.done(); // finishing the test. }) } });
  30. @morlhon / @wayofspark #DV13-#jswebtesting CasperJS – In a few words

    - French developer ! ! - Adds an higher level API to the power of PhantomJS ! - Very fast ! - Export results to xUnit format ! - Easy to extend - Test API not really mature (limited setup / teardown usage... Even on 1.1 branch) ! - Python or Ruby launcher, can be problematic for Window (but is this really an issue ?)
  31. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js ! • It’s a HEADLess

    emulated browser • Runs on Node.js • Uses many emulation libraries ! • v1.4.1 (2.0 forthcoming) • MIT Licence
  32. @morlhon / @wayofspark #DV13-#jswebtesting Emulation ? Zombie.js is not a

    « true » browser, it uses carefully selected emulation libraries to simulate all the stuff a browser does : ! ! • JSDom by Elijah Insuas (Dom Emulation) • HTML5 by Aria Stewarts (HTML5 Parsing) • Sizzle.js by John Resig (Css Selectors) • ...
  33. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js Ok so it’s a low

    end browser ? • HTML5 Markup • HTML5 form fields (search, url etc...) • CSS3 Selectors • Cookie & Web Storage • XMLHttpRequest • setTimeout/setInterval • pushState, popState & hashchange • event, alert, confirm & prompt • WebSockets & Server-Sent events
  34. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js Fluent API ! var Browser

    = require("zombie"); ! browser = new Browser(); browser.visit("http://localhost:3000/", function () { ! browser.fill("email", "[email protected]"); browser.fill("password", "youDontWantToKnow"); ! browser.pressButton("Login", function() { assert.ok(browser.success); assert.equal(browser.text("title"), "Welcome To Brains Depot"); }); ! });
  35. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js Promises support : browser =

    new Browser(); browser.visit(home) .then(function() { browser.clickLink("Panier"); }).then(function() { browser.success.should.equal(true); browser.text("h2.font").indexOf("panier est vide").should.equal(1); });
  36. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js Using css selectors browser.queryAll("#content >

    .produit").length.should.equal(7); browser.text("#nb-article").should.equal('1 article');
  37. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js When there is no hope…

    expect(browser.evaluate('$(\'input[name="name"]\').val()')).to.equal('FooBar');
  38. @morlhon / @wayofspark #DV13-#jswebtesting Zombie.js ! • Ultra Fast •

    Fluent API • Test code is a *joy* to read • Emulates a Browser • Does not work on Windows • Errors are somewhat cryptic • Integrating this in a java build is not easy. • Main developer says "it’s a side project"
  39. @morlhon / @wayofspark #DV13-#jswebtesting QUnit • Very light and easy

    to use test framework ! • Initially developed by John Resig (jQuery) • Became public in 2008 ! • Browser based test launcher ! • Format xUnit ! • Extensible (plugins)
  40. @morlhon / @wayofspark #DV13-#jswebtesting QUnit <html> <head> <title>Hello QUnit</title> <link

    rel="stylesheet" href="/qunit.css"> </head> <body> <div id="qunit"></div> <div id="qunit-fixture"></div> <script src="/qunit.js"></script> <script src="/tests.js"></script> </body> qunit.html module("Hello.World"); ! test("universe should agree", function() { ok(42==42, "probably true"); }); ! module("JS.Troll"); ! test("equality should be trolling material", function() { equal('\n\r\t' , 0, "a whitespace string is equal to zero"); }); ! test("transitivity should be trolling material", function() { equal(0, "", "zero is equal to empty string"); equal(0, "0", "zero is equal to the string '0'"); notEqual("", "0", "the empty string is not equal to the '0' string"); }); tests.js Very simple :
  41. @morlhon / @wayofspark #DV13-#jswebtesting Assertions There are only a few

    : • ok() • equal() • deepEqual() • strictEqual() • throws() • notEqual / not[...] ! Yep, that's all ! // ok() ok(3 > 2); // pass ok(1==2); // fail ! // equals() equal("same value", "same value"); // pass equal("some value", "another value"); // fail ! // deepEqual() var a = { someValue : 2 }; var b = { someValue : 2 }; equal(a, b); // fail because a and b have not the same reference. deepEqual(a, b); // pass because a and b have the same attributes. ! // strictEqual() equal(1, true); // pass because (1==true) is true strictEqual(1, true); // fail because (1===true) is false" ! // throws() throws(function() { throw "thrown"; }, "thrown"); // pass because expected exception is raised.
  42. @morlhon / @wayofspark #DV13-#jswebtesting Asynchronous tests asyncTest and start usage

    asyncTest("stuff and remote work loading", function() { ! var stuff = new Stuff(); ok(!stuff.readyToWork); ! $.get("/some/work/url", function(work) { ! stuff.loadWork(work); ! ok(stuff.readyToWork); ! start(); // telling the test to start. }); ! });
  43. @morlhon / @wayofspark #DV13-#jswebtesting Ecosystem Various plugins : • Assertions

    (canvas, html...) • Runners (junit) • Integration (drupal, rails) • Framework integration (sinon, jsTestDriver) • Themes Example of assertion definition QUnit.extend( QUnit.assert, { /** * given number should be strictly greater than given minimum */ greaterThan : function(number, minimum, message) { QUnit.push(number > minimum, number, ">" + minimum, message); } }); ! // […] using the assertion in a test test("testing my assertion", function(assert) { var five = 5; var three = 3; assert.greaterThan(five, three, "5 should be greater than 3"); });
  44. @morlhon / @wayofspark #DV13-#jswebtesting Sinon.JS Standalone test spies, stubs and

    mocks for JavaScript http://sinonjs.org http://github.com/cjohansen/Sinon.JS
  45. @morlhon / @wayofspark #DV13-#jswebtesting Sinon.JS • Test library for javascript

    ! • Created by Christian Johansen ( @cjno ) – Creator of juicer, core developer of Buster.js – Author of « Test-Driven Javascript Development » ! • Initial release in june 2010 – Actual version : 1.7.3 ( 20/06/2013 ) ! • No dependencies, works on any environment (browser, node...)
  46. @morlhon / @wayofspark #DV13-#jswebtesting Features • Spies, Stubs, Mocks !

    • Fake Timers ! • Fake Ajax Requests ! • Fake Server ! • Sandboxing ! • Test assertions / Matchers
  47. @morlhon / @wayofspark #DV13-#jswebtesting Spies var spy = sinon.spy(); !

    spy("hello"); spy("world"); ! spy.called // returns true spy.calledOnce // returns false spy.calledTwice // returns true ! spy.withArgs("hello").called // returns true spy.withArgs("foo").called // returns false ! spy.getCall(0).args // returns ["hello"] ! spy.withArgs("hello").calledBefore(spy.withArgs( "foo")) ; // returns true ! spy.calledOn(window) ; // true, default context - Allows spying of functions : - number of calls - call parameters - call context (this) - thrown exceptions - calls order - Allows spy 'filtering' by args ! - Informations on each call
  48. @morlhon / @wayofspark #DV13-#jswebtesting var stub = sinon.stub(); ! stub.returns("stubbed");

    stub.withArgs("hello").returnsArg(0); stub.withArgs("throw").throws("Woups"); ! stub(); // returns "stubbed" stub("dolly"); // returns "stubbed" stub("hello"); // returns "hello" stub("throw"); // throws Woups ! // this work too stub.withArgs("hello").calledOnce; stub.threw("Woups"); // returns true Stubs Adds execution behaviour to spies - Spy API usable - Add stubbing methods var stuff = { work : function() { return "ok";} }; ! // replacing object method with a stub var stub = sinon.stub(stuff, "work"); ! stub.returns("stubbed"); ! stub(); // returns "stubbed" greeter.greet(); // this works too ! stub.restore(); // original method restored stuff.work(); // return "ok" again
  49. @morlhon / @wayofspark #DV13-#jswebtesting test("mock should mock", function() { var

    greeter = { greet : function(name) { console.log("hello " + name); } }; var mock = sinon.mock(greeter); // expects greet to be called once with 'john' mock.expects("greet").withArgs("john").once(); ! // mock.verify() // would raise greeter.greet("john"); mock.verify(); // will pass });
 Mocks and expectations - Mock an object converts every methods to expectations. ! - An expectation : - extend stub (and spy) API - adds calls validation methods ! Mock.restore() allow to cleanup and restore original object behaviour
  50. @morlhon / @wayofspark #DV13-#jswebtesting var clock = sinon.useFakeTimers(); var spy

    = sinon.spy(); ! window.setTimeout(spy, 150); ! clock.tick(149); spy.called; // returns false ! clock.tick(1); spy.called; // returns true ! // with setInterval : var clock = sinon.useFakeTimers(); var spy = sinon.spy(); ! window.setInterval(spy, 30); clock.tick(100); ! spy.calledThrice; // returns true 
 Fake Timers Synchronous implementation of javascript timers ! Allows easier testing of asynchronous code ! Under the hood : replaces setTimeout and setInterval methods. ! Can also mock the Date object and prototype, but with ‘limitations' (moment.js...)
  51. @morlhon / @wayofspark #DV13-#jswebtesting var xhr = sinon.useFakeXMLHttpRequest(); var requests

    = []; xhr.onCreate = function (request) { requests.push(request); }; ! var spy = sinon.spy(); ! $.get("/").then(spy); ! requests[0].respond(200, {}, "some text"); ! spy.calledOnce; // returns true spy.withArgs("some text").calledOnce; // returns true Fake XHR Replaces the XHR object with a synchronous implementation. ! Allows synchronous handling of ajax requests without 'real' server. ! Possibility to choose which requests to mock (using filters)
  52. @morlhon / @wayofspark #DV13-#jswebtesting // mock XHR with the fake

    server var server = sinon.fakeServer.create(); ! server.respondWith("GET", "/", // method & url [200, // response code { "Content-Type": "text/plain" }, // headers "fake response"] // response content ); ! var spy = sinon.spy(); ! $.get("/").then(spy); ! spy.called; // returns false server.respond(); // respond to received requests ! spy.calledOnce; // returns true spy.withArgs("fake response").calledOnce; // returns true Fake server - High level API to handle fake ajax requests ! - Allows to specify response by url and/or method ! - Can return static content or delegates the response logic to a function.
  53. @morlhon / @wayofspark #DV13-#jswebtesting Sandboxing var sandbox = sinon.sandbox.create(); !

    sandbox.useFakeTimers(); sandbox.useFakeServer(); // all code from there uses fake timers and server. ! // […] var spy = sandbox.spy(someObject, “method”); ! sandbox.restore(); // original behaviour is now restored // sandbox spies are also removed Allows isolation and cleanup of faking features ! Test classes inherits from Sandbox.
  54. @morlhon / @wayofspark #DV13-#jswebtesting // using sinon in qunit test

    test("sinon adapter should work", function () { ! var stub = this.stub(); stub.returns("stubbed"); ! var result = stub(); ! equal(result, "stubbed", "result value should be stubbed"); ok(stub.calledOnce, "spy should have been called once"); }); Adapters Allows partial or complete Sinon.js API integration in other test frameworks ! - Jasmine - Chai - QUnit - NodeUnit - ... Sinon-qunit usage example
  55. @morlhon / @wayofspark #DV13-#jswebtesting Stubs doesn't do proxyfication (no easy

    way to call original method in stubbed one) ! Beware of Date faking when using date libraries ! Ajax faking is not that great on IE6/7 (strange isn't it?) 
 In a few words... Extremely powerful ! Play (very) well with others ! Fluent and readable API ! Spies and stubs quickly become essentials in tests.
  56. @morlhon / @wayofspark #DV13-#jswebtesting Karma • Multi Browser Test Runner

    • Runs on node.js • Allows to run unit and end-to-end (acceptance) tests • Developed by a member of the angular.js team. ! • v0.10 • MIT Licence
  57. @morlhon / @wayofspark #DV13-#jswebtesting Karma Is able to handle many

    browsers, at the same time : • Chrome • Firefox • Opera • PhantomJS • InternetExplorer
  58. @morlhon / @wayofspark #DV13-#jswebtesting KarmaJS • Supports many testing tools

    • Mocha • Jasmine • Qunit • Angular Scenario !
  59. @morlhon / @wayofspark #DV13-#jswebtesting Karma Funky Configuration File : basePath

    = ''; files = [ ANGULAR_SCENARIO, ANGULAR_SCENARIO_ADAPTER, '*.coffee' ]; reporter = 'progress'; port = 8080; runnerPort = 9100; colors = true; logLevel = LOG_DEBUG; autoWatch = true; browsers = ['Chrome']; singleRun = false; proxies = { '/': 'http://localhost:4516/' } urlRoot = '/karma/';
  60. @morlhon / @wayofspark #DV13-#jswebtesting Karma Angular Scenario Example : it

    'should show the title', -> browser().navigateTo '/serpodile' expect(element('title').text()).toBe 'Serpodile'
  61. @morlhon / @wayofspark #DV13-#jswebtesting Karma Angular Scenario : ! •

    Works only with Angular.js application • No CallBack Handling (Yeeepeee !!!!) • Every element is a future : expect(element('title').text()).toBe 'Devoxx'
  62. @morlhon / @wayofspark #DV13-#jswebtesting Karma ! • Multi-Browser • Angular

    Scenario • Quite Fast • Watch files and run tests continuously • End 2 End testing on angular only • Documentation is "ok" at best • Browser must be installed separately • Verbose configuration file • Protractor coming in ? !
  63. @morlhon / @wayofspark #DV13-#jswebtesting Mocha • Test Framework • Runs

    in Node.js Or In a browser • Able to run synchronous & asynchronous tests • Out of the box integration with Jenkins, TeamCity, Junit • chai.js compatible ! • 1.13.0 • MIT Licence
  64. @morlhon / @wayofspark #DV13-#jswebtesting mocha test example assert = require('assert')

    ! describe 'mocha example', -> ! it 'should uppercase the 1st letter', -> assert.equal('Foo', uppercaser('foo')) ! it 'should render uppercase even if its already done', -> assert.equal('Foo', uppercaser('Foo'))
  65. @morlhon / @wayofspark #DV13-#jswebtesting Mocha + chai.js ! • Ultra

    fast • Fluent API • Testing code is readable ! • No browser emulation (scenario) • Java Build Integration is heavy • Chai’s Should assertion can be seen as intrusive
  66. @morlhon / @wayofspark #DV13-#jswebtesting chai.js • Test assertion library •

    Supports 3 assertions test style • Runs on node.js (no browser needed) ! • 1.8.1 • MIT Licence
  67. @morlhon / @wayofspark #DV13-#jswebtesting Chai.js : Expect style var assert

    = chai.assert; ! assert.typeOf(devoxx, 'string'); assert.equal(devoxx, 'Awesome'); assert.lengthOf(devoxx, 7) var expect = chai.expect; ! expect(devoxx).to.be.a('string'); expect(devoxx).to.equal('Awesome'); expect(devoxx).to.have.length(7); chai.should(); ! devoxx.should.be.a('string'); devoxx.should.equal('Awesome'); devoxx.should.have.length(7); should.not.exist(undefined); Should style : Assert style :
  68. @morlhon / @wayofspark #DV13-#jswebtesting Buster.js • Test runner with some

    refreshing features • Still in beta (latest : 0.7.4) • Multiple launch modes : – Node module – Browser capture ( like jsTestDriver or Karma) – Embed static server – In browser ( experimental ) – Headless with PhantomJS ( not yet implemented ) • Allows to create and manage multiple test contexts • Ships with sinon.js
  69. @morlhon / @wayofspark #DV13-#jswebtesting Tests describe("my stuff", function () {

    before(function() { this.stuff = new Stuff(); }); it("should exists", function () { expect(this.stuff).toBeDefined(); }); it("should be fluffy", function() { expect(this.stuff.isFluffy()).toBe(tru e); }); }); buster.testCase("project.Stuff", { setUp: function () { this.stuff = new Stuff(); }, "my stuff should exists" : function () { assert.defined(this.stuff); }, "my stuff should be fluffy" : function() { assert(this.stuff.isFluffy()); } }); Two syntaxes to write tests : xUnit format BDD format
  70. @morlhon / @wayofspark #DV13-#jswebtesting Nested test cases buster.testCase("project.Stuff", { !

    setUp: function () { this.stuff = new Stuff(); }, ! "new stuff" : { "is working" : function() { assert(this.stuff.working()); } }, ! "broken stuff" : { setUp : function() { this.stuff.broken = true; }, ! "is not working" : function() { refute(this.stuff.working()); } } }); When Unit tests meets BDD...
  71. @morlhon / @wayofspark #DV13-#jswebtesting Test context configuration config["My tests"] =

    { env: "browser", // or "node" extensions: [require("buster-coverage")], // extensions "buster-coverage": { outputDirectory: "coverage_reports" // any plugin config }, rootPath: "../", libs : [ // list of libs to use for tests "lib/jquery.js", "lib/underscore.js" ], sources: [ // list of source files "sources/**/*.js" // Glob patterns supported ], tests: [ // List of test files. "test/*-test.js" ], resources: [ // list of server resources available to tests. { path: "/todo-items", // proxy resource to remote server backend: "http://localhost:8000/todo/todo-items" }, { path: "/user.json", // mocked resource content: JSON.stringify({ id: 1, name: "Christian" }), headers: { "Content-Type": "application/json" } } ] }; - describes test context ! - handles plugins ! - no html required ! - exposes static resources ! - can proxy resources from other servers
  72. @morlhon / @wayofspark #DV13-#jswebtesting New features assert.isNotNull(); // doesn't exist

    ! refute.isNull(); refute instead of assert.not[...] buster.testCase("My thing", { requiresSupportFor: { "touch events": typeof(document.body.ontouchstart) != "undefined", "XHR": typeof(XMLHttpRequest) != "undefined" }, ! "touch events should trigger an ajax call": function () { // .. } }); Disable tests with // in name Tests prerequisites buster.testCase("projet.deferred", { ! "this test will be executed": function () { assert(true); }, ! "// this test will not launch": function () { assert(false); } });
  73. @morlhon / @wayofspark #DV13-#jswebtesting In a few words… Still very

    young ( beta 0.7 ) ! Lot of 'not yet implemented' features – Window support – Headless testing – Custom “test bed” Already very feature rich for a beta ! Some great new / fresh ideas ! Seems very promising
  74. @morlhon / @wayofspark #DV13-#jswebtesting JSCover Code coverage tool for javascript

    http://tntim96.github.com/JSCover http://github.com/tntim96/JSCover
  75. @morlhon / @wayofspark #DV13-#jswebtesting JSCover - Javascript code coverage tool

    ! - Java rewrite of JsCoverage (was c++) ! - Two instrumentation modes : - build time - runtime using proxy ! - Export to Cobertura format (for integration in Jenkins / CI)
  76. @morlhon / @wayofspark #DV13-#jswebtesting JSCover - Does what it's supposed

    to do ! - Active developer ! - Results can be integrated to CI - Integration in test phase can be (well, is) tricky in both modes ! - plain old jar, not in maven repositories for now...
  77. @morlhon / @wayofspark #DV13-#jswebtesting Plato - Easy Install (NPM) !

    - Easy to use ! - Beautiful Dashboard - Reports only available in html or json. No other format to export. ! - So very difficult to integrate in a CI (not possible)
  78. @morlhon / @wayofspark #DV13-#jswebtesting • JS Test Driver • Selenium

    (fluentlenium) • HtmlUnit • Jasmine • Istanbul Let’s not forget…
  79. @morlhon / @wayofspark #DV13-#jswebtesting For Unit Testing : Mocha +

    chai.js Mocha + QUnit + Sinon.js (Mock, Spy, Stubs) Jasmine or QUnit (browser needed) Wrap Up
  80. @morlhon / @wayofspark #DV13-#jswebtesting Wrap Up For User Interface Testing

    Zombie.js ( Emphasis on test readability ) Casper.js ( Emphasis on reliability ) Karma ( For using angular.js, keep an eye on ProTractor though )
  81. @morlhon / @wayofspark #DV13-#jswebtesting Wrap Up In the radar :

    Buster.js Zombie.js downfall ? ProTractor