Javascript Test Dummies

210a2116d2266c84d155f1d8a14f31ef?s=47 FEVR
May 19, 2016

Javascript Test Dummies

Mauro Verrocchio @ FEVr

210a2116d2266c84d155f1d8a14f31ef?s=128

FEVR

May 19, 2016
Tweet

Transcript

  1. Javascript Test Dummies image source: http://imgkid.com/crash-test-dummy-symbol.shtml Mauro Verrocchio @maur8ino

  2. image source: http://imgkid.com/crash-test-dummies-logo.shtml

  3. $ whoami Mauro Verrocchio Senior Frontend Engineer @ Gild twitter:

    @maur8ino github: @maur8ino likes: #allthingsjavascript #ruby #python #golang #testing #skiing #hiking #playingbasketball ...and a #nerddaddy :)
  4. Why talking about frontend testing? • the so called “Web

    2.0” gave a lot of attention to the frontend • lots of responsive UI and SPA • tighter release cycles • tools and frameworks are grown and become mature
  5. image source: http://blog.teamtreehouse.com/wp-content/uploads/2014/01/dev-tools-console.png Debugging on frontend image source: http://blogs.sitepointstatic.com/images/tech/520-opera-dragonfly-1.png image

    source: http://cdn.sixrevisions.com/0228-01_firebug_guide_webdesigners_thumbnail.jpg
  6. We will talk about... • unit testing • end-to-end testing

  7. Unit testing “In computer programming, unit testing is a software

    testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.” source: https://en.wikipedia.org/wiki/Unit_testing
  8. Javascript most notable unit testing framework history • JSUnit (2001)

    developed by Pivotal Labs (https://en.wikipedia.org/wiki/JSUnit) • QUnit (~2006) initially developed by John Resig as a part of jQuery, used to test jQuery, jQuery UI and jQuery Mobile (http://qunitjs.com/) • Jasmine (2010) developed by Pivotal Labs (http://jasmine.github.io/) • Mocha (2011) initially developed by TJ Holowaychuk (http://mochajs.org/)
  9. Javascript most notable unit testing framework history #2 • node-tap

    (2011) developed by TapJS (https://github.com/tapjs/node-tap) • tape (2012) developed by James Halliday (substack) (https://github.com/substack/tape) • AVA (2014) developed by Sindre Sorhus (https://github.com/sindresorhus/ava/) More information about Test Anything Protocol (TAP) (https://en.wikipedia.org/wiki/Test_Anything_Protocol)
  10. A simple project • a small library to get users

    and repositories from Github using the REST api • a couple of React components https://github.com/maur8ino/fevr-talk-may-2016
  11. The Github REST api • to get user’s repositories list:

    GET https://api.github.com/users/{username}/repos • to get user’s specific repository info: GET https://api.github.com/repos/{username}/{reponame} • more info at https://developer.github.com/v3/
  12. The Github REST api #2 • for unauthenticated requests, the

    rate limit allows you to make up to 60 requests per hour (https://developer.github.com/v3/#rate-limiting) • you can cache the response and use ETag or Last-Modified returned values in a subsequent request passing If-None-Match or If-Modified-Since in the header, getting a 304 if the response is not modified; a 304 response does not count against rate limit (https://developer.github.com/v3/#conditional-requests)
  13. current window.fetch support source: http://caniuse.com/#feat=fetch window.fetch polyfill https://github.com/github/fetch

  14. current Promise(s) support source: http://caniuse.com/#feat=promises Promise polyfill https://github.com/jakearchibald/es6-promise

  15. <GithubSearch/> Application structure <SearchForm/> <SelectForm/> <DisplayInfo/> props: disabled value handleSubmit

    props: disabled values handleSubmit props: value state: loading repoList selectedUser selectedRepo methods: getUserReposList getUserRepo methods: handleSubmit methods: handleSubmit
  16. July 2015 • XMLHttpRequest • React v0.13.x • Babel v5.x

    • Karma + Browserify + Babelify + Rewireify + Mocha + Chai + Sinon Then and now... May 2016 ➔ fetch() ➔ React v15.x ➔ Babel v6.x + presets ➔ AVA (jsdom) + fetchMock + babel- plugin-rewire + Sinon
  17. React library • it needs no presentation :) • has

    a TestUtils class that makes easy to test components https://facebook.github.io/react/
  18. AVA “Futuristic test runner” • minimal and fast • simple

    test syntax • enforces writing atomic tests • runs tests concurrently • no implicit globals https://github.com/sindresorhus/ava
  19. Sinon.JS • standalone test spies, stubs and mocks for JavaScript

    • no dependencies, works with any unit testing framework http://sinonjs.org/ http://ricostacruz.com/cheatsheets/sinon.html
  20. image source: http://memegenerator.net/instance2/604560

  21. Challenges • running in a concurrent environment may have problems

    with globals • sharing the same variable between tests must be passed in a test context • babel-plugin-rewire must be applied to source files • shallow rendering does not yet support refs
  22. { [...] "ava": { "require": [ "babel-register", "./test/helpers/setup-browser-env.js" ], "babel":

    "inherit" }, "babel": { "presets": [ "es2015", "react" ], "env": { "testing": { "plugins": [ "rewire" ] } } }, [...] } package.json
  23. { [...] "scripts": { "test": "NODE_ENV=testing ava --verbose", "test-watch": "NODE_ENV=testing

    ava --watch", [...] }, [...] } package.json
  24. Github library

  25. import 'whatwg-fetch'; import {Promise} from 'es6-promise'; const githubBaseURL = 'https://api.github.com';

    export function getUserReposListURL(username) { if (!username) { throw new Error('Username is undefined, null or an empty string'); } return `${githubBaseURL}/users/${encodeURIComponent(username)}/repos`; }; src/github.js
  26. import test from 'ava'; import * as github from '../src/github';

    test('should generate the list of repositories url', t => { t.is(github.getUserReposListURL('maur8ino'), 'https://api.github.com/users/maur8ino/repos'); t.is(github.getUserReposListURL('maur/8i&no'), 'https://api.github.com/users/maur%2F8i%26no/repos'); }); test/github-test.js
  27. [...] test('should throw an error', t => { let fn

    = github.getUserReposListURL.bind( github.getUserReposListURL, undefined ); t.throws(fn, /empty string/); fn = github.getUserReposListURL.bind( github.getUserReposListURL, null ); t.throws(fn, /empty string/); fn = github.getUserReposListURL.bind( github.getUserReposListURL, '' ); t.throws(fn, /empty string/); }); test/github-test.js
  28. const cache = {}; const get = (url) => {

    let options = {}; if (cache[url] && cache[url].ETag) { options.headers = { 'If-None-Match': cache[url].ETag }; } return fetch(url, options).then((response) => { if (response.status === 200) { if (response.headers.get('ETag')) { cache[url] = { ETag: response.headers.get('ETag') }; } return response.json(); } else if (response.status === 304) { if (cache[url].json) { return Promise.resolve(cache[url].json); } } else { return Promise.reject(Error('Error', response)); } }).then((json) => { if (cache[url]) { cache[url].json = json; } return Promise.resolve(json); }); }; src/github.js
  29. export function getUserReposList(username) { try { return get(getUserReposListURL(username)); } catch(error)

    { return Promise.reject(error); } }; src/github.js
  30. [...] import fetchMock from 'fetch-mock'; [...] test.afterEach(() => { fetchMock.restore();

    }); [...] test.serial('should make an ajax request', t => { fetchMock.mock('https://api.github.com/users/maur8ino/repos', 'GET', [ { "id": 35957173, "name": "angular-post-message" }, { "id": 37024234, "name": "react-bem-mixin" } ]); return github.getUserReposList('maur8ino').then(response => { t.deepEqual(response, [ { id: 35957173, name: 'angular-post-message' }, { id: 37024234, name: 'react-bem-mixin' } ]); }); }); test/github-test.js
  31. React Components

  32. global.document = require('jsdom').jsdom('<body></body>'); global.window = document.defaultView; global.navigator = window.navigator; test/helpers/setup-browser-env.js

  33. import React from 'react'; const SearchForm = React.createClass({ handleSubmit(e) {

    e.preventDefault(); this.props.handleSubmit(this.refs.input.value); }, render() { let {value, disabled} = this.props; return ( <form className="username" onSubmit={this.handleSubmit}> <input type="text" ref="input" defaultValue={value} disabled={disabled} /> <button type="submit" disabled={disabled}>Search</button> </form> ); } }); export default SearchForm; src/SearchForm.jsx
  34. import test from 'ava' import sinon from 'sinon'; import React

    from 'react'; import ReactDOM from 'react-dom'; import {Simulate, renderIntoDocument, findRenderedDOMComponentWithTag} from 'react-addons-test-utils'; import SearchForm from '../src/SearchForm.jsx'; test('should be initialized with a default value', t => { const searchForm = renderIntoDocument(<SearchForm value="maur8ino" />); const input = findRenderedDOMComponentWithTag(searchForm, 'input'); t.is(input.value, 'maur8ino'); }); test/SearchForm-test.jsx
  35. [...] test('should disable the form', t => { const searchForm

    = renderIntoDocument(<SearchForm disabled={true} />); const input = findRenderedDOMComponentWithTag(searchForm, 'input'); const button = findRenderedDOMComponentWithTag(searchForm, 'button'); t.true(input.disabled); t.true(button.disabled); }); test('should handle the form submit', t => { const handleSubmit = sinon.spy(); const searchForm = renderIntoDocument( <SearchForm handleSubmit={handleSubmit} value="maur8ino" /> ); Simulate.submit(ReactDOM.findDOMNode(searchForm)); t.true(handleSubmit.calledOnce); t.true(handleSubmit.calledWith('maur8ino')); }); test/SearchForm-test.jsx
  36. Unit testing with AngularJS https://github.com/maur8ino/aduno-talk-september-2015

  37. Tip of the day: “Proof of concept: make sure your

    tests work by letting them fail under different circumstances.”
  38. End-to-end testing “System testing [End-to-end] of software or hardware is

    testing conducted on a complete, integrated system to evaluate the system's compliance with its specified requirements. System testing [End-to-end] falls within the scope of black box testing, and as such, should require no knowledge of the inner design of the code or logic.” source: https://en.wikipedia.org/wiki/System_testing
  39. Selenium testing suite • initially developed at ThoughtWorks by Jason

    Huggins in 2004 • automate web browsers across many platforms • de-facto standard for testing end-to-end web applications • Selenium Webdriver, maybe the main reason why Selenium is popular, accepts command in “Selenese” and communicates with real browser • Selenium IDE has a Firefox addon to record, edit and playback browser interaction • Selenium Grid acts as a hub for Webdriver instances http://docs.seleniumhq.org/
  40. Cucumber BDD tool • lets you write feature file in

    natural language (Gherkin) • implemented in Ruby, Javascript, Java, Python, .NET, etc.. http://cucumber.io/
  41. Feature file • has a Feature keyword, which explains the

    feature • can have a Background list of steps to do for every Scenario • has one or more Scenarios • every step of Background and Scenarios starts with Given, When, Then, But or And keywords; there is no actual difference between them, they let you describe even better the feature
  42. Step definition file • one or more step in feature

    file can be resolved by one step definition (because of regular expression) • lets you use all the power of the language • drives real browser via selenium or headless browser like zombiejs or phantomjs
  43. Test end-to-end with Cucumber • npm install --save-dev cucumber selenium-

    webdriver • feature files will be placed in features/ • step definition files will be placed in features/steps • support files will placed in features/support
  44. image source: http://memegenerator.net/instance/62951843

  45. Integration with a CI server .travis.yml http://docs.travis-ci.com/user/languages/javascript-with-nodejs/ language: node_js node_js:

    - “5.1” - “4.2” - “...” env: - MY_ENV_VAR=”something”
  46. Final take: let the machine do the machine work

  47. What’s next... • test coverage (Istanbul - https://gotwarlost.github.io/istanbul/) (nyc -

    https://github.com/bcoe/nyc) • code quality (bitHound - https://www.bithound.io/) (Code Climate - https://codeclimate.com/)
  48. Useful links • http://stackoverflow.com/a/680713 Javascript unit test frameworks overview •

    https://github.com/cucumber/cucumber-js the “official” homepage of cucumberjs with examples • http://devdocs.io/ awesome documentation • http://kangax.github.io/compat-table/es6/ super useful es6 table comparison • http://svgporn.com/ developer tools icon in svg
  49. “It is now two decades since it was pointed out

    that program testing may convincingly demonstrate the presence of bugs, but can never demonstrate their absence. After quoting this well-publicized remark devoutly, the software engineer returns to the order of the day and continues to refine his testing strategies, just like the alchemist of yore, who continued to refine his chrysocosmic purifications.” (E. W. Dijkstra, 1988) source: https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD1036.html Quote of the day:
  50. source: http://2.bp.blogspot.com/_7OeU3GAGUBI/S9DZdiChuoI/AAAAAAAAACE/-CJ6H_oL2Ck/s1600/thats+all+folks.jpg Thanks!