# What to test in JS? ## A humble overview of JS testing techniques
# Jisse Reitsma - Founder of Yireo - Trainer of M2 devs - Creator of MageTestFest & Reacticon - Member of ExtDN.org - Magento Master 2017 & 2018 Mover - A simple Dutch boy
# Fundamentals - No inline JS code (?) - Move logic to separate JS components - Use RequireJS to load dependencies - Pass HTML identifiers as configuration (JSON)
# Levels - **Unit**: Jasmine, Mocha, QUnit - **Integration**: RequireJS - **Functional**: MFTF - **End-to-end**: Selenium, Nightwatch.js
# Diving into the tools
# How to make a choice?
# Which environment? - Regular webbrowser - PhantomJS browser - Node CLI - Rhino CLI
# Testing frameworks - Jasmine - MochaJS - NodeUnit - Tape - CasperJS - Jest - Intern - QUnit
# Assertion libraries - Chai - Should.js - Expect.js - NodeJS `assert()` - power-assert - assert-plus - unexpected - better-assert
# Test runners - Karma - (Tape) - Ava
# Other tools -
Sinon
Mocking & spying library
-
ESLint
Linting your JS code
-
Mockjax
Mocking AJAX requests
# Could you repeat that?
# Find your way
# Jasmine basics
# Getting started with Jasmine Installing packages: ```bash $ npm install -g jasmine-node $ npm install -g yo $ npm install -g generator-jasmine ``` Creating a Jasmine skeleton: ```bash $ yo jasmine ``` Files: - `test/index.html` - `test/spec/test.js`
# Adding Karma
# Adding Karma Installing packages: ```bash $ npm install -g karma $ npm install -g generator-karma $ npm install karma-chrome-launcher ``` Creating a Karma/Jasmine skeleton ```bash $ yo karma --test-framework=jasmine ```
# Karma config for Jasmine File `karma.conf.js`: ```js module.exports = function(config) { 'use strict'; config.set({ basePath: '', frameworks: ['jasmine'], files: ['spec/*.js'], port: 8080, browsers: ['Chrome'], plugins: [ 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-jasmine' ] }); }; ```
# The test (Jasmine) File `test/spec/test.js`: ```js (function () { 'use strict'; describe('Give it some context', function () { describe('maybe a bit more context here', function () { it('should run here few assertions', function () { expect(true).toBe(true); }); }); }); })(); ```
# Run tests ```bash $ cd test/ $ karma start $ karma start karma.conf.js ```
# Adding Mocha
# Karma + Mocha Installing packages: ```js $ npm install karma-mocha mocha ```
# Karma config for Mocha File `karma.conf.js`: ```js module.exports = function(config) { 'use strict'; config.set({ basePath: '', frameworks: ['mocha'], files: ['spec/*.js'], port: 8080, browsers: ['Chrome'], plugins: [ 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-mocha' ] }); }; ```
# Run tests Mocha only: ```bash $ node_modules/mocha/bin/mocha test/spec/test.js ``` or via Karma: ```bash $ cd test/ $ karma start $ karma start karma.conf.js ```
# The test (Mocha) File `test/spec/test.js`: ```js (function () { 'use strict'; const assert = require('assert'); describe('Give it some context', function () { describe('maybe a bit more context here', function () { it('should run here few assertions', function () { assert.equal(true, 1); }); }); }); })(); ```
# Adding Chai
# Adding Chai Installing packages: ```bash $ npm install karma-chai ```
# Karma config for Mocha+Chai File `karma.conf.js`: ```js module.exports = function(config) { 'use strict'; config.set({ basePath: '', frameworks: ['mocha', 'chai'], files: ['spec/*.js'], port: 8080, browsers: ['Chrome'], plugins: [ 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-mocha' ] }); }; ```
# Run tests Mocha only: ```bash $ node_modules/mocha/bin/mocha test/spec/test.js ``` or via Karma: ```bash $ cd test/ $ karma start $ karma start karma.conf.js ```
# The test (Mocha+Chai) File `test/spec/test.js`: ```js (function () { 'use strict'; const assert = chai.assert; describe('Give it some context', function () { describe('maybe a bit more context here', function () { it('should run here few assertions', function () { assert.equal(true, 1); }); }); }); })(); ```
# From Karma to Tape
# Adding Tape Installing packages: ```bash $ npm install -g tape ``` Run tests: ```bash $ tape test/spec/test.js ```
# The test (Tape) File `test/spec/test.js`: ```js (function () { 'use strict'; const test = require('tape'); test('Give it some context', function (t) { t.equal(typeof true, 'boolean'); t.end(); }); })(); ```
# Some thoughts
# Some thoughts - Mocha or Tape for Node-based unit tests - Jasmine+Karma or Mocha+Karma for browser-based tests - And use Chai if you want a bit more spice - Or use some other tool that suits you - Or use Selenium or MFTF for functional tests - Or everything at once
# We don't care
which tool you use
# Whatever works for you - Tests should easy and fast - Pick one tool & setup. Stick with it. - Keep your tests simple
# Asking the right questions
# What do you want to touch? - Mocking the DOM (with `domjs`?) - Mocking jQuery - Spying
# Magento JS component JS component: ```js define(['jquery'], function($) { myComponentFunction = function(config, element) { $(element).append('Hello World'); } return { 'my-component': myComponentFunction } }); ``` PHTML template: ```html
```
# Preparing test Installing packages: ```bash npm install jsdom npm install jquery npm install requirejs ``` MAJOR FAIL: `jQuery requires a window with a document` RECOMMENDATION: Use Jasmine anyway
## Mocking jQuery or just using it? #### Or maybe it is too hard to mock jQuery?
## How to test methods hidden using the revealing module pattern? #### Don't test them? Treat it as a black box?
## Depending on RequireJS or faking it? #### Beware of the NodeJS versioning nightmare
# Refactoring - Dependency Injection (RequireJS) - Limit dependencies per JS component - Keep JS component small - Separate JS component per purpose - General-purpose library - Pure functions - Model - View
## Each part has its own way of testing
# Thanks ## @jissereitsma / @yireo #### http://slides.yireo.com/yireo/js_testing
# Cat slide
Your browser does not support the video tag.