Tag Archives: gulp

Continuous Functional Test Automation with Gulp, Mocha, Request, Cheerio, Chai

In this post, I have shown how to build a platform-agnostic, continuous server-side functional test automation infrastructure that can test just about any website regardless of the server (eg. IIS/Apache) hosted, application platform (eg. ASP.NET/Java/Python/what not) and operating systems (eg. Windows/Linux) used, on & off Azure, using JavaScript which is the most open scripting language on this planet now, obviously all powered by Node.js. I hope to cover more advanced testing scenarios in future posts.

One of the most essential parts of a Node.js application lifecycle management is test automation. You must watch out for code breaks. Especially when the app goes big and complex and when all you ever want to do is coding or focusing on solving problems, you really don’t want to test code manually. This is a waste of time and productivity. But, we are programmers and we want our test code to test our code. In this post, I have shown how you can perform server-side functional tests least but not limited to testing DOM values, etc., without launching a browser, however, though it will not be able to test client-side functionalities. I have covered a bit of Gulp, Mocha, Request and Cheerio in order to perform functional tests on a Node.js app. It’s important to note that we’re not going to test code, rather test the functionality of our app, and at the same time, similar results if not better can be achieved by record/write & replay using Selenium as well, and there’re more eg. PhantomJs/Zombie.js, but that I might cover in future posts.

Overview of the modules

  1. Gulp is a build system, which will assist in running the test code as part of the build. It can watch for file changes and trigger test automatically. Popular equivalent of Gulp is is Grunt. There’re various reasons why I prefer Gulp over Grunt, which is outside the scope of this post.
  2. Mocha is a test framework, which gives the instruments we need to test our code. Popular alternative to Mocha is Jasmine.
  3. Request is one of the most popular module that can handle HTTP request/response.
  4. Cheerio is a cool module that can give you a DOM from HTML as string.
  5. Chai is a fine assert module.

Execute the following instructions to install Gulp and Mocha into the app:

npm i mocha gulp gulp-mocha gulp-util -g
npm i mocha gulp gulp-mocha gulp-util --save

The web app to test

Consider a simple Express + Node.js app that we’re putting under test, which has a few buttons and clicking on them will navigate to relevant pages. If no such page is found, a Not Found page will be displayed.

formdata

We’ll test whether the page loads properly with the expected text in the body, and clicking on Signup and Login redirect user to respective pages.

Setting up Mocha

Mocha’s expectation is that we create a ‘test’ folder and keep all the tests there. I have gone ahead and created another folder inside of ‘test’ called ‘functional.’ Now that I am going to test the home page of the app, I have also created a file called home.js where our test code related to testing the home page will reside. I have written the following code there:

process.env.NODE_ENV = 'test';

describe('Home page', function () {
    it('should load the page properly');
    it('should navigate to login');
    it('should navigate to sign up');
    it('should load analytics');
});

Here’s another reason why I love Visual Studio Code so much, because it allows me resolve the dependencies just like below:

VSCode

I have gone ahead and chosen the first choice, which has resulted into this:

/// <reference path="../../typings/mocha/mocha.d.ts"/>
/// <reference path="../../typings/node/node.d.ts"/>
process.env.NODE_ENV = 'test';

describe('Home page', function () {
    it('should load the page properly');
    it('should navigate to login');
    it('should navigate to sign up');
    it('should load analytics');
});

Visual Studio Code has included the type definition of the references we are using, and referred inside the .js file. I have indicated NODE_ENV an app-wide constant to inform that we’re currently in test mode, which is often useful inside the app code to determine the current running mode. More on that might be covered in future posts. Mocha facilitates us in writing specs in describe-it way. Consider these as placeholders for now, as we will look into it in a while. For now, lets say, these are our specs and we want to integrate into our build system. Now if I execute “mocha test/functional/home.js” the test will run as expected:

Mocha

That’s not convenient, especially when you will have many test code and which may possibly reside inside various folder structures. In other words, we want it to run recursively. We can achieve just that, by creating a file test\mocha.opt with the following parameters as content:

--reporter spec
--recursive

Now if you execute mocha you will find the same results as previous. If you have noticed I have specified a reporter here called ‘spec’ – you can also try with nyan, progress, dot, list and what not in order to change the way Mocha reports test results. I like spec, because it gives me Behavior Driven Development (BDD) flavor.

Integrating with Gulp

Now that we have a test framework running, we’d like to include this as part of the build process, which can even report us of code breaks during development time. In order to do that lets go ahead and create a gulpfile.js at the root with the following contents:

var gulp = require('gulp');
var mocha = require('gulp-mocha');
var util = require('gulp-util');

gulp.task('test', function () {
    return gulp.src(['test/**/*.js'], { read: false })
        .pipe(mocha({ reporter: 'spec' }))
        .on('error', util.log);
});

gulp.task('watch-test, function () {
    gulp.watch(['views/**', 'public/**', 'app.js', 'framework/**', 'test/**'], ['test']);
});

Gulp is essentially a task runner. It can run defined tasks. If ‘gulp’ command is executed, it will search for ‘default’ task and execute that. Since, we didn’t declare any ‘default’ task, rather ‘test’ task, we need to specify the task name as parameter, for example, ‘gulp test’ on the command line in order to achieve the same result that we did with mocha. Second task that we have defined, with the name ‘watch-test’ watches out for the folders that I have specified here, views, public and test for file changes, if it finds any, it automatically run the ‘test’ task and report the test results. I have also included app.js which is my main Node.js file, and framework folder, where I like to put all my Node.js code. Lets go ahead execute the following:

gulp watch-test

Now if you make any change to any files located in the paths above, you will see something similar to the following:

Gulp

As you can see all of our tests are still pending, lets go ahead and write some tests on our setup now. We need to refer back to the test/functional/home.js file. Let us implement two simple tests, first one to succeed, and the latter to fail. I’m using Node’s assert module here to report satisfied/unsatisfied conditions.

var assert = require('assert');
process.env.NODE_ENV = 'test';

describe('Home page', function () {
	it('should load the page properly', function()
		{
			assert.ok(true);
		});

	it('should navigate to login', function()
		{
			assert.equal(2 == 4);
		});

	it('should navigate to sign up');
	it('should load analytics');
});

This should ideally result in the following:

Gulp2

Testing functionality with Request, Cheerio, Chai

Now that we’re set with the test infrastructure, let us write our specification to “actually” test the functionality. Unlike PhantomJs/Zombie.js, we are not going to change a lot of the way we have learned to write tests as of now and also it won’t require any external libraries/runtime/frameworks, eg. Python. It will also not require us to go through test framework version management nightmares. Lets go ahead and install a few more Node.js modules:

npm i request cheerio chai -g
npm i request cheerio chai --save

If you ever get to work with PhantomJs/Zombie.js/Selenium, you will see in how many places you need to change code in order to get your test code up and running. I have built this test infrastructure in order to remove all such pain and streamline the process. The only place I have to change is the test/functional/home.js file, and the rest will play along nicely.

/// <reference path="../../typings/mocha/mocha.d.ts"/>
/// <reference path="../../typings/node/node.d.ts"/>
process.env.NODE_ENV = 'test';

var request = require('request'),
	s = require('string'),
	cheerio = require('cheerio'),
	expect = require('chai').expect,
	baseUrl = 'http://localhost:3000';

describe('Home page', function () {
	it('should load properly', function (done) {
		request(baseUrl, function (error, response, body) {
			expect(error).to.be.not.ok;
			expect(response).to.be.not.a('undefined');
			expect(response.statusCode).to.be.equal(200);

			var $ = cheerio.load(body);
			var footerText = $('footer p').html();
			expect(s(footerText).contains('Tanzim') && s(footerText).contains('Saqib')).to.be.ok;
			done();
		});
	});

	it('should navigate to login', function (done) {
		request(baseUrl + '/login', function (error, response, body) {
			expect(error).to.be.not.ok;
			expect(response).to.be.not.a('undefined');
			expect(response.statusCode).to.be.equal(200);
			expect(s(body).contains('Not Found')).to.be.not.ok;
			done();
		});
	});

	it('should navigate to signup', function (done) {
		request(baseUrl + '/signup', function (error, response, body) {
			expect(error).to.be.not.ok;
			expect(response).to.be.not.a('undefined');
			expect(response.statusCode).to.be.equal(200);
			expect(s(body).contains('Not Found')).to.be.not.ok;
			done();
		});
	});
});

The code here is quite self-explanatory. I have used Request module to GET different paths of my website. I have checked for HTTP response code and if there was any error. I have used jQuery-like DOM manipulation to retrieve resulting HTML, and also used another nice module called string in order to check the string values. Cheerio was used to very conveniently load a DOM from the resulting HTML that was returned in response. And, they were all reported via chai library using “expect” flavor.

How to run it

Running it is also quite easy. Just run our application, in this case, my app is written in Node.js:

npm start

And, in another console/command prompt, run the test:

gulp test

Here’s the test results now:

Final

Source code

I will try to continue building this project and here’s the github address: https://github.com/tsaqib/formdata and live demo is here: http://formdata.azurewebsites.net.

Advertisements

Getting Started with JavaScript-based Full Stack Development

In this post, I have tried to introduce several JavaScript-based development tools with which you can start writing web and mobile apps.

Node.js

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

2000px-Node_js_logo_svg

You can go ahead and install it in your system that will open up a whole new JavaScript-based ecosystem including a nice package management system where you can install/update/uninstall packages including their dependencies from a command-line environment. Because of that, Node.js is also considered this planet’s the ultimate command line tool development platform. Give it a whirl: http://nodejs.org/

Here’s a sample hello world server – think of it as a website that serves itself in response to HTTP requests:

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Now in order to run your new Node.js application, here’s the command that you may want to issue, assuming you have saved the file as app.js:

node app.js

This Node.js code opens a server at 1337 port and serves this page when requested at: http://127.0.0.1:1337.

Node.js Package Systems

Node.js allows us to use Node Package Manager (npm) from command line to manage necessary dependencies. Tons of packages can be found from here: https://www.npmjs.com/.

Before we go ahead and install a package we need to create a package.json file which will contain information about our project and will be used to maintain dependencies and their versions. We do not need to create it manually, rather we need to answer a series of questions asked by the following command, which you can just pass entering Enter every time so that it can prepare a set of default settings which is fine for now:

npm init

This will create a package.json file that we had been intending to create. Go ahead and open it – you will find something like as follows:

{
  "name": "nodetest",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
  },
  "author": "",
  "license": "ISC"
}

Now let us begin with installing the first package. For example: string package offers rich string manipulation features. Let us go ahead and install that:

npm install –g string 
npm install string --save

The first instruction will add the package to the global level, meaning that whenever next time a project would require that the next instruction as above can be issued to retrieve it from the (system-wide) globally available package store stored in your computer. By adding a –save parameter, we are telling that the dependency should be recorded into the package.json as well. Here’s the updated json:

{
  "name": "nodetest",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "dependencies": {
    "string": "^3.0.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;&amp; exit 1"
  },
  "author": "",
  "license": "ISC"
}

Now let’s use the newly added package in the app.js code:

var S = require('string');
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('World exists in the string Hello World: ' + S('Hello World').contains('World'));
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

When hit from the browser, this code will produce the following output:

World exists in the string Hello World: true

Yeoman

Yeoman is a scaffolding tool – what this essentially means is that it can quickly create a boilerplate sample of many JavaScript based libraries/frameworks out there. At the time of typing Yeoman has generators for 1100+ projects. For full reference: http://yeoman.io.

yeoman-logo

I am not a big user of it, however it’s easier to get started on such projects that use full-stack JavaScript technologies. Install Yeoman using the following command:

npm install -g yo

Creating an ASP.NET Web API application using Yeoman

Let us now use the  Yeoman to scaffold an ASP.NET Web API application meaning that it will ask you a series of few questions about your new ASP.NET Web API project and then it will go ahead and create all the directories and files and organize them in order for you to use right away opening up Visual Studio.

npm install -g generator-webapi
yo webapi

Untitled

Creating a Firefox OS app using Yeoman

Here are the set of commands that you can execute:

npm install -g generator-firefox-os  
yo firefox-os

Bower

As you can imagine such JavaScript-based ecosystem comes at a price of plugin management nightmare, however Bower is a yet another tool that aims to solve some of its management problems.

687474703a2f2f692e696d6775722e636f6d2f516d47485067632e706e67

You will be able to find a searchable list of packages here, or you can search from command line as well:

bower search polymer
Search results:

    polymer git://github.com/Polymer/polymer.git
    webcomponentsjs git://github.com/Polymer/webcomponentsjs.git
    polymer-elements git://github.com/Polymer/polymer-elements.git
    ... a big list of polymer related packages ...

Let us create a polymer application which is based on Android’s new Lollypop UI. Much like npm, we need to create a bower.json file using similar command. It will ask a few questions, feel free to answer or leave them.

bower init
bower install -g --save Polymer/polymer

It will create a folder called bower_components where all the plugins will reside and you may refer from your web project. Should you require to update any of the components you may issue the following command:

bower update

Using a package

Let us create a simple HTML page where we will use rickshaw a nice graphing utility. Installing rickshaw:

bower install –g --save rickshaw

Here’s the HTML that renders just nice:

<html>
    <head>
        <link rel="stylesheet" href="bower_components/rickshaw/rickshaw.min.css"/>
        <script src="bower_components/rickshaw/vendor/d3.v3.js"></script>
        <script src="bower_components/rickshaw/rickshaw.min.js"></script>
    </head>
    <body>

        <div id="chart"></div>

        <script>

            var graph = new Rickshaw.Graph( {
                element: document.querySelector("#chart"),
                width: 500,
                height: 200,
                series: [{
                    color: 'red',
                    data: [
                        { x: 0, y: 40 },
                        { x: 1, y: 49 },
                        { x: 2, y: 38 },
                        { x: 3, y: 30 },
                        { x: 4, y: 32 } ]
                }]
            });

            graph.render();

        </script>
    </body>
</html>

Untitled1

By the way, most of these js libraries have really nice mascots/logos. After you have mastered this pattern of development, you can easily dive into many stacks available out there. For example, MEAN = MongoDB + Express + AngularJs + Node.js, CEAN = CouchDB + Express + AngularJs + Node.js and what not. Here’s a guide on how CouchDB + Express + Node.js can play together: CRUD with CouchDB in Node.js, another one tweaking default Express application.