Complete flow testing in

Linters

"Good Practices"

# Tools

Sass, Css

Scsslint

Csslint

 

Javascript

Eslint

Jshint

Jslint

# .jshitrc

{
    // JSHint Default Configuration File (as on JSHint website)
    // See http://jshint.com/docs/ for more details

    "maxerr"        : 50,       // {int} Maximum error before stopping

    // Enforcing
    "bitwise"       : true,     // true: Prohibit bitwise operators (&, |, ^, etc.)
    "camelcase"     : true,    // true: Identifiers must be in camelCase
    "curly"         : true,     // true: Require {} for every new block or scope
    "eqeqeq"        : true,     // true: Require triple equals (===) for comparison
    "forin"         : false,     // true: Require filtering for..in loops with obj.hasOwnProperty()
    "immed"         : false,    // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
    "indent"        : 2,        // {int} Number of spaces to use for indentation
    "latedef"       : true,    // true: Require variables/functions to be defined before being used
    "newcap"        : true,    // true: Require capitalization of all constructor functions e.g. `new F()`
    "noarg"         : true,     // true: Prohibit use of `arguments.caller` and `arguments.callee`
    "noempty"       : true,     // true: Prohibit use of empty blocks
    "nonew"         : true,    // true: Prohibit use of constructors for side-effects (without assignment)
    "plusplus"      : false,    // true: Prohibit use of `++` & `--`
    "quotmark"      : "single",    // Quotation mark consistency:
                                //   false    : do nothing (default)
                                //   true     : ensure whatever is used is consistent
                                //   "single" : require single quotes
                                //   "double" : require double quotes
    "undef"         : true,     // true: Require all non-global variables to be declared (prevents global leaks)
    "unused"        : true,     // true: Require all defined variables be used
    "strict"        : true,     // true: Requires all functions run in ES5 Strict Mode
    "trailing"      : true,    // true: Prohibit trailing whitespaces
    "maxparams"     : false,    // {int} Max number of formal params allowed per function
    "maxdepth"      : false,    // {int} Max depth of nested blocks (within functions)
    "maxstatements" : false,    // {int} Max number statements per function
    "maxcomplexity" : false,    // {int} Max cyclomatic complexity per function
    "maxlen"        : false,    // {int} Max number of characters per line

  
  // Relaxing
    "asi"           : false,     // true: Tolerate Automatic Semicolon Insertion (no semicolons)
    "boss"          : false,     // true: Tolerate assignments where comparisons would be expected
    "debug"         : false,     // true: Allow debugger statements e.g. browser breakpoints.
    "eqnull"        : false,     // true: Tolerate use of `== null`
    "es5"           : false,     // true: Allow ES5 syntax (ex: getters and setters)
    "esnext"        : false,     // true: Allow ES.next (ES6) syntax (ex: `const`)
    "moz"           : false,     // true: Allow Mozilla specific syntax (extends and overrides esnext features)
                                 // (ex: `for each`, multiple try/catch, function expression…)
    "evil"          : false,     // true: Tolerate use of `eval` and `new Function()`
    "expr"          : true,     // true: Tolerate `ExpressionStatement` as Programs
    "funcscope"     : false,     // true: Tolerate defining variables inside control statements"
    "globalstrict"  : false,     // true: Allow global "use strict" (also enables 'strict')
    "iterator"      : false,     // true: Tolerate using the `__iterator__` property
    "lastsemic"     : false,     // true: Tolerate omitting a semicolon for the last statement of a 1-line block
    "laxbreak"      : false,     // true: Tolerate possibly unsafe line breakings
    "laxcomma"      : false,     // true: Tolerate comma-first style coding
    "loopfunc"      : false,     // true: Tolerate functions being defined in loops
    "multistr"      : true,      // true: Tolerate multi-line strings
    "proto"         : false,     // true: Tolerate using the `__proto__` property
    "scripturl"     : false,     // true: Tolerate script-targeted URLs
    "smarttabs"     : true,      // true: Tolerate mixed tabs/spaces when used for alignment
    "shadow"        : false,     // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
    "sub"           : true,      // true: Tolerate using `[]` notation when it can still be expressed in dot notation
    "supernew"      : false,     // true: Tolerate `new function () { ... };` and `new Object;`
    "validthis"     : false,     // true: Tolerate using this in a non-constructor function

    // Environments
    "browser"       : true,     // Web Browser (window, document, etc)
    "couch"         : false,    // CouchDB
    "devel"         : true,     // Development/debugging (alert, confirm, etc)
    "dojo"          : true,    // Dojo Toolkit
    "jquery"        : false,    // jQuery
    "mootools"      : false,    // MooTools
    "node"          : true,    // Node.js
    "nonstandard"   : false,    // Widely adopted globals (escape, unescape, etc)
    "prototypejs"   : false,    // Prototype and Scriptaculous
    "rhino"         : false,    // Rhino
    "worker"        : false,    // Web Workers
    "wsh"           : false,    // Windows Scripting Host
    "yui"           : true,    // Yahoo User Interface

    // Legacy
    "nomen"         : true,     // true: Prohibit dangling `_` in variables
    "onevar"        : false,    // true: Allow only one `var` statement per function
    "passfail"      : false,    // true: Stop on first error
    "white"         : false,    // true: Check against strict whitespace and indentation rules

    // Custom Globals
    "predef"        : [ ]       // additional predefined global variables
}

  

# Plugins

Sublime text 3 -> Sublime Linter ScssLint

Sublime text 3 -> Sublime Linter Jshint

Sublime text 3 -> Sublime Linter Eslint

Sublime text 3 -> Sublime Linter Jslint

 

# Integration

Linter On Save

Automatize lint code task as a first task in workflow

 

Build

"Manage Dependencies"

# Tools

Manage packages

Bower

 

Build/Dist modules

Closure compiler

RequireJS

Browserify

# Bower

Package manager

Plain dependencies tree

Private bower to company packages

 

 

## manage packages

## atomic build

## plain tree

Middle Earth app

              bower_components

                  Vassal

                      King

                      Knight

                      Shield bearer

 

## private bower

Middle Earth app

                   .bowerrc

              

                  

 

    
   {
      "registry": {
        "search": [
          "http://localhost:8000",
          "https://bower.herokuapp.com"
        ]
      }
   }

# Build modules

"Dependencies injection"

 

 

## Closure compiler

Load modules in develop

"test should be FAST, console is fast"

Closure library

 

Build all modules in a file to dist

Closure compiler

 

### Common JS module notation


    goog.provide('app.middleEarth');
    goog.require('app.Vassal');
    goog.require('app.King');
    goog.require('app.Knight');
    goog.require('app.ShieldBeaver');    

    app.middleEarth = function () {
         
       'use strict';

      var vassal,
          king,
          knight,
          shieldBeaver;

       var initializeDependencies = function() {
          vassal = new app.Vassal();
          king = new app.King();
          knight = new app.Knight();
          shieldBeaver = new app.ShieldBeaver();
       };

       // ...
       //  Some functions 
       // ...

       var initialize = function () {
          initializeDependencies();
       };

       return {
          initialize: initialize
       };

    };

## Require JS

Load modules in develop

"test should be FAST"

AMD module notation

 

Build all modules in a file to dist

r.js

 

### amd module notation


    define(function (require) {
        // Load any app-specific modules
        // with a relative require call,
        // like:
        var messages = require('./messages');
    
        // Load library/vendor modules using
        // full IDs, like:
        var print = require('print');
    
        print(messages.getHello());
    });

## Browserify

require in browser

if we wanna run test with console have a problem

    
    <script src="bundle.js"></script>

    <script>
      var through = require('through');
      var duplexer = require('duplexer');
      var myModule = require('my-module');
      /* ... */
    </script>

Test

  "Test everything"

Unit Test

  "Show me the money"

# Tools

Runners

PhantomjsKarma


Test frameworks

Jasmine, Mocha, Preamble, Jest, Intern, Bootcamp(sass)


# Extra tools

Asserts syntax

Chai -> BDD / TDD assertion library 

 

Test doubes

Sinonjs -> Standalone test spies, stubs and mocks

 

Test Browser fake DOM

 

Jasmine-jquery -> BDD / TDD assertion library 

 

Zombie -> Insanely fast, full-stack, headless browser testing using node.js 

 

 

 

 

# Status check vs behavoir check

State testing. An action is executed and ask our system, by asserts system objects are checked. (Chicago school).

 

Behavior testing. System interaction is verified with others externs systems and asserts are checked whether they have implemented the systems, methods and parameters (London school).

If you want to test interactions with employees have to test a single test

# Test doubles

 

Dummy. Objects are passed around but never actually used. Usually they are just used to fill parameter lists.

 

Fake. Objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).

 

 

 

 

 

Mocks. Are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive

 

Stub. Provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'

 

 

 

 

## Code example

   
   // My System module

   'use strict';
    
    var System = function(authoriser) {
    
      
        var loginCount = 0;
      
        var login = function(username, password) {
            if (authoriser.authorise(username, password)) {
                loginCount++;
            }
        };
       
        var loginCount =  function() {
            return loginCount;
        };
    
        return {
            login: login,
            loginCount: getLoginCount
        };
    
    };
   
   // Module Authoriser, a System module dependency

   'use strict';

   
    var Authoriser = function() {

        var authorize = function(username, password) {

            // ...
            // connects to LDAP or some database, 
            // verifies user credentials
            // returns true if the username/password 
            // pair is valid, false otherwise
            // ...

        };

        return {
            authorize: authorize
        };

    };
    
   

 

How can we prove that a newly created System has no logged in users? And do we even need to construct a real Authoriser object for that?

Since we know that calling system.loginCount does not even touch the authoriser object, we can replace the authoriser with a dummy.

 

"And that’s all there is to a dummy! You pass it into something when you have to provide an argument, but you know that it will never be used".

### Dummy

   
   // test code to system module getloginCount method

   'use strict';

   
    describe('A newly created System', function() {

        var dummyAuthoriser = { };
    
        it('has no logged in users', function() {
            var system = new System(dummyAuthoriser);    
            expect(system.getloginCount()).to.equal(0);
        });

    });
    
   

 

Let’s now suppose that you want to test a part of your System that requires you to be logged in.

Of course you might say that you could just log in, but you’ve already tested that login works, and since the process of logging in takes time, why do it twice? Also, if there’s a bug in login your test would fail too! That’s an unnecessary coupling that can be easily avoided with a stub.

 

Martin Fowler Test Double says : "Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test."

 

### Stub

   
    // test code to system module getloginCount 
    // method with one user is authenticated
    // injecting a stub authoriser 
    // EXAMPLE WITHOUT SINONJS

    'use strict';

    describe('A System with a logged in user', function() {    
        
        var stubAcceptingAuthoriser = { 
            authorise: function() { 
                return true; 
            }
        };    
        
        it('has a loginCount of 1', function() {
            var system = new System(stubAcceptingAuthoriser);    
            system.login('bob', 'SecretPassword');    
            expect(system.getloginCount()).to.equal(1);
        });

    });
   
   
    // test code to system module getloginCount 
    // method with one user is authenticated
    // injecting a stub authoriser 
    // EXAMPLE WITH SINONJS

    'use strict';

    describe('A System with a logged in user', function() {    
        
        var stubAcceptingAuthoriser = sinon.createStubInstance(Authoriser);
        stubAcceptingAuthoriser.authorise.returns(true);
        
        it('has a loginCount of 1', function() {
            var system = new System(stubAcceptingAuthoriser);    
            system.login('bob', 'SecretPassword');    
            expect(system.getloginCount()).to.equal(1);
        });

    });
   

 

How can we test whether or not this interaction really takes place? What we’d need to do is spy on the caller to see inside the workings of the algorithm we’re testing – and that’s precisely what spies are for.

You inject a spy object in exactly the same way you’d inject a stub, but then at the end of your test you check if the interactions recorded by your spy are the ones you expected it to see.

 

Martin Fowler in Test Double says : "Spies are stubs that also record some information based on how they were called."

 

### Spy

   
    // test code to system module login 
    // method delegates logging users in to the authoriser
    // injecting a spy authoriser 
    // EXAMPLE WITHOUT SINONJS

    'use strict';

    describe('A System', function() {

        var spyAcceptingAuthoriser = {    
            wasAuthoriseCalled: false,    
            authorise: function() {
                this.wasAuthoriseCalled = true;    
                return true;
            }
        };
    
        afterEach(function() {
            spyAcceptingAuthoriser.wasAuthoriseCalled = false
        };
    
        it('delegates logging users in to the authoriser', function() {
            var system = new System(spyAcceptingAuthoriser);    
            system.login('bob', 'SecretPassword');    
            expect(spyAcceptingAuthoriser.wasAuthoriseCalled).to.equal(true);
        });

    });
   
    // test code to system module login 
    // method delegates logging users in to the authoriser
    // injecting a spy authoriser 
    // EXAMPLE WITH SINONJS

    'use strict';
      
    describe('A System', function() {

        var authoriser,
            system,
            spyAuthorise;

        authoriser    = new Authoriser(),
        system        = new System(authoriser),
        spyAuthorise = sinon.spy(authoriser, 'authorise');

        it('delegates logging users in to the authoriser', function() {
            system.login('bob', 'SecretPassword');    
            expect(spyAuthorise).to.have.been.called;
        });

    });

 

Robert Martin (Uncle Bob) in The Little Mocker says : "A mock is not so interested in the return values of functions. It’s more interested in what function were called, with what arguments, when, and how often."

 

Another thing that makes a mock different from a stub is its verify method, which groups all the assertions and performs them at the end of a test, verifying our System's behaviour:

Now that we understand how mocks work and we know how to build them ourselves, let’s see how sinon can make things easier.

 

 

 

### Mock

   
    // test code to system module login 
    // injecting a mock authoriser 
    // EXAMPLE WITHOUT SINONJS

    'use strict';

    describe('A System', function() {

        var mockAcceptingAuthoriser = {    
            wasAuthoriseCalled: false,    
            authorise: function() {
                wasAuthoriseCalled  = true;
                return true;
            },    
            verify: function() {
                expect(wasAuthoriseCalled).to.equal(true);
            }
        };
    
        it('delegates logging users in to the authoriser', function() {
            var system = new System(mockAcceptingAuthoriser);    
            system.login('bob', 'SecretPassword');    
            mockAcceptingAuthoriser.verify(); // assertions moved to #verify
        });

    });

Christian Johansen in Test Spies, Stubs and Mocks says : "In Sinon, the basic unit of faking is functions, always functions. So a “stub object” in Java-type literature translates to a “stub function/method” in Sinon/JavaScript land.."

 

   
    // test code to system module login 
    // injecting a mock authoriser 
    // EXAMPLE WITH SINONJS

    'use strict';

    describe('A System', function() {
        var authoriser = new Authoriser(),
            mockAcceptingAuthoriser = sinon.mock(authoriser);
    
        afterEach(function() {
            mockAcceptingAuthoriser.restore();
        });
    
        it('delegates logging users in to the authoriser', function() {
            // prepare
            var system = new System(authoriser);
    
            // expect
            mockAcceptingAuthoriser.expects('authorise').once().returns(true);
    
            // act
            system.login('bob', 'SecretPassword');
    
            // assert
            mockAcceptingAuthoriser.verify();
        });
    }); 

 

Robert Martin (Uncle Bob) in The Little Mocker says : "Fake has business behavior. You can drive a fake to behave in different ways by giving it different data".

 

Suppose we wanted to define several personas to whom our System responded differently. Let’s say “bob” knows his password and “alice” forgot it.

My personal recommendation would be to use two separate stubs, but let’s talk about fakes as some might prefer to use them.

 

### Fake

   
    // test code to system module login 
    // injecting a fake authoriser 
    // EXAMPLE WITHOUT SINONJS

    'use strict';

    describe('A System', function() {

        var fakeAuthoriser = {
            authorise: function(username, password) {
                return (username === 'bob' && password === 'SecretPassword');
            }
        },
    
        var system = new System(fakeAuthoriser);
    
        it('allows Bob in', function() {
            system.login('bob', 'SecretPassword');    
            expect(system.getLoginCount()).to.equal(1);
        });
    
        it('does not allow Alice in', function() {
            system.login('alice', 'password');    
            expect(system.getLoginCount()).to.equal(0);
        });

    });

   
    // test code to system module login 
    // injecting a mock authoriser 
    // EXAMPLE WITH SINONJS

    'use strict';

    describe('A System', function () {

        var fakeAuthoriser = sinon.createStubInstance(Authoriser);
            
        fakeAuthoriser.authorise
            .returns(false)
            .withArgs('bob', 'SecretPassword').returns(true),
    
        system = new System(fakeAuthoriser);
    
        it('allows Bob in', function () {
            system.login('bob', 'SecretPassword');    
            expect(system.loginCount()).to.equal(1);
        });
    
        it('does not allow Alice in', function () {
            system.login('alice', 'password');    
            expect(system.getLoginCount()).to.equal(0);
        });

    });

### Fake Server

   
    'use strict'; 

    var RESPONSE = {
        city: "TOLEDO",
        state: "OK"
    };

    beforeEach( function () {
        server = sinon.fakeServer.create();
        server.respondWith( 'POST', '/example/practice', [ 200, {
            'Content-Type': 'application/json'
        },
        JSON.stringify( RESPONSE )      
    ] );
    server.respond();
    } );

    describe('A post request', function() {         
        it('request is good', function() {
           expect( server.requests.length ).toEqual( 1 );  
           expect( request.url ).toEqual( '/example/practice' );
           expect( request.method ).toEqual( 'POST' );
           expect( request.requestHeaders.Accept ).toEqual( 'application/json' );
           expect( request.requestHeaders[ 'Content-Type' ] ).toEqual( 'application/json;charset=utf-8' );
           expect( request.requestBody ).toEqual( JSON.stringify( {
            name: "TOLEDO",
            state: "OK"
          } ) );         
       });
    });

Aceptation Test

  "Show me the money"

# Tools

Behaviour Driven Development tool

CucumberCucumberjs 

 

Webdriver

Selenium, Selenium-webdriver, Webdriverjs

 

End to end

Nightwatchjs, Proctractor 

CSS

Phantomcss, Hardy

 

 

 

Metrics

  "Measure everything"

# Local tools

# Remote tools

SonarQube

 

 

//sonar-project.properties

sonar.projectKey=rmb-ui-widgets-datepicker
sonar.projectName=RMB-UI-WIDGETS-DATEPICKER
sonar.projectVersion={package.version}
sonar.sources=src/js
sonar.tests=test
sonar.junit.reportsPath=reports/junit
sonar.javascript.lcov.reportPath=reports/coverage/lcov/lcov.info

Deploy

  "No Pirlo no party"

If tests aren't green, deploy fail

 

C.I

  "Continous Integration"

# Tools

The flow

  "No Pirlo no party"

linters

preprocess

unit tests

buid

postprocess

local metrics

# Dev

linters

preprocess

unit tests

buid

postprocess

sonar metrics

deploy

fuctional test

# Deploy Env

Automatize

  "The same task for all"

# Tools

# Grunt

Modules

Your gruntfile can be some modules with gruop task splited, build, deploy, etc

 

Build your particle 

Build your particle with all tasks for your flow and import with your project bower.json like a dependency.

# Grunt vs Gulp

Grunt

A lot of plugins

 

Gulp

Better syntax, pipe concept, faster

@javierland

  

Complete flow test in Javascript

By Javier López de Ancos

Complete flow test in Javascript

Complete test flow , TDD, BDD for javascript work

  • 1,749