# 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 - Organizer of usergroups, hackathons - Magento Master 2017 & 2018 Mover - I live in a palace
# I live in a palace ...
# Houtje Touwtje
# Houtje Touwtje - Benefits - Little effort to get started - You need little material
# Houtje Touwtje - Downsides - False easily apart - Hard to re-structure - Performance testing means you need to start again
# Testing is key
# JavaScript testing is harder
# Testing a JS component in M2
Say there is a HTML element, where `data-mage-init` leads to a RequireJS alias that leads to a JS component of which the return value is either a function or an object, while in either case, it is "executed" so that it returns value is turned into a Knockout ViewModel, which is then automagically bound to the original HTML area, so all properties can be represented using a weird KO syntax (with not 1, not 2 but 3 possible stylings), while underneath the ViewModel could be observed and modified by multiple observers, using either original KO code, or imports, or exports, or both, in a way that even the most experienced Magento developers find disturbing to say the least.
- And now, how to test this?
# Focus on logic or output?
# Magento JS component JS component: ```js define(['jquery'], function($) { myComponentFunction = function(config, element) { $(element).append(config.message); } return { 'my-component': myComponentFunction } }); ``` PHTML template: ```html
```
# What is output? - HTML generated by Magento - Interpreted DOM tree - DOM modified by Knockout
# Levels - **Unit**: Jasmine, Mocha, QUnit - **Integration**: same (but with RequireJS) - **Functional**: Selenium, CasperJS, 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
# 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(); }); })(); ```
# Ok, ok, but what to do?
# 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
# BONUS: React testing - Use shallow component rendering - Compare output (from HTML back to JSX?) - Testing component properties - Or use full HTML rendering to test for complete output
# My personal approach - Tape for bare-bones functionality - Mocha for more complex stuff - Selenium / CasperJS for functional tests
# Ideal procedure for unit tests - Install extension (without Magento) - Run `npm install` - Run `npm test` See [Yireo_GoogleTagManager2](https://github.com/yireo/Yireo_GoogleTagManager2) extension for inspiration
# GTM2 code ```js define([...], function (...) { ... return { 'initDataLayer': initDataLayer, 'getCustomer': getCustomer, 'getQuote': getQuote, 'getOrder': getOrder, 'isLoggedIn': isLoggedIn, 'getCustomerSpecificAttributes': getCustomerSpecificAttributes, 'addScriptElement': addScriptElement, 'yireoGoogleTagManager': function (config) { ... } }; }); ```
# 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
## Each part has its own way of testing
# Thanks ## @jissereitsma / @yireo ### http://slides.yireo.com/yireo/js_testing2
# Cat slide
Your browser does not support the video tag.