2015년 9월 6일 일요일

Javascript Continuous Integration


We'll see how to setup javascript continuous intergration environment using jenknin, git, gerrit.

1. Add PhantomJS to  package.json

PhantomJS can execute javascript without browser. Need to add PhantomJS to run test-case upon PhantomJS


npm install phantomjs --save-dev
npm install karma-phantomjs-launcher --save-dev
npm install karma-junit-reporter --save-dev


2. Create separated karma config file

We'll create separate karma config file for jenkins. Let's create "jenkins-ci-conf.js" like followings. Set browser to phantomjs like line [line 63].




3. Create grunt task for jenkins CI

Create grunt task for Jenkins. In this example, I create "continuous" task. Look at [line 23 ~ 26] closely. Change karma config file to "jenkins-ci-conf.js". We have to use "singleRun". The "singleRun" options means that Continuous Integration mode. You can exectue the task like following command,
grunt karma:continuous


4. Build PhantomJS [If jenkins is installed linux]

If you jenkins is installed to linux, you need to build it. But you use other environment, you don't need to build. Refer PhantomJS in detail.


5. Install jenkins plugins [If jenkins is installed linux]

Install Environment Injector Plugin. It is useful to configure PhantomJS path.


6. Job configuration

Let's see job configuration. I used git as a source code management.

Source code management



















build trigger






gerrit trigger




























Build environment
set phantomJS path in script content



















Build
There are two steps in build script. The first one is install dependents module using npm install. The second one is execute grunt task like "grunt karma:continuous"














Action after build
Set path to generate test result report












7. execute result.

































javascript development environment

We'll see how to setup javascript development and test environment using npm, grunt, karma, jasmine.


1. nodejs

We will not use nodejs directly. But there are many useful tool which operated on nodejs platform. Let's install nodejs first. You can easily install nodejs after downloading binary it.


You can install nodejs for linux(Ubuntu 14.04.02 LTS) like followings,

Download node-install.sh
wget https://github.com/taaem/nodejs-linux-installer/releases/download/v0.3/node-install.sh

Add exe authority 
chmod +x node-install.sh

Execute
./node-install.sh


2. npm and package.json

The npm helps developer to share their code and  to reuse module. Those code called package or module. They are shared by registering npm registry. The npm registry is a kinds of database which contains package information. The package is a module directory. The package.json is in that folder, this fils contains meta information of package. When you create javascript application, you might use those package(module). With npm and package.json, you can easily compose those module and share environment with your colleague.

3. package.json

You can crate package.json using following command. It doesn't matter create it manually. Refer this.


npm init

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (myjavascriptproject) hellojavascript
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Some/Pth/package.json:

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


Is this ok? (yes) yes

4. karma & jasminie

karam is automatic test executor which is made by AngularJS team of google. You can install in following command,

npm install karma --save-dev

We will write test-code using jasmine javascript test framework. Refer the usage of jasmine which i wrote before.

npm install karma-jasmine --save-dev

With --save-dev, all dependent module are updated to package.json automatically. We'll add required module this way. We can execute test-case just using karma itself. But in this post, we will execute karma using grunt.

Let's make karma configuration file.

karma init my.conf.js

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> 

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.

> yes

You can define src files and test file to include like [line 17]


5. grunt

Install grunt which is javascript build tool. Grunt is useful for many reasons. Let's install following modules.

install grunt 
npm install grunt --save-dev
npm install grunt-cli --save-dev

grunt-karma plugin
npm install grunt-karma --save-dev

To run test-case in chrom browser, neet to install following plug-in.
npm install karma-chrome-launcher --save-dev

Install addtional plug-ins. jshint display javascirpt grammer warning and errors

npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-watch --save-dev

Grunt configuration
Gruntfile.js is a grunt confugration file. In [line 4], Grunfile.js read package.json. From the next line, configure jshint, watch, karma task. You can run specific task like followings,

grunt [task name]

run karma task.

grunt karma

6. directory structure and package.json

All directory structure which i used like followings,























The node_modules directory don't need to manage by source manage tool(git or svn). Because you can create same directory structure using package.json with npm. When you do build and test using CI tool like jenkins, this is a quite convenient. Let's simply test it. Copy package.json to another directory and execute following command.

npm install

Then npm will install required module after reading package.json



Javascript object model and inheritance


Let's see Javascript Object model and Inheritance. We'll see Javascript specific things and something is good to know. Learning test is written using Jasmine. Please refer Jasmine usage which I wrote before if you have interest it. 

Class based object model and prototype based object model
   
There is clear concept in programming language which use class based object model. It is class and instance. The class abstract characteristics of  objects which are in specific set(or category) using member variable and method. For example, The "Talent class" abstract general attributes of who have talented some area. On the other hand,  The instance represents one of talented people. "SteaveJobs instance" is that the actual instance of "Talent class" type. It have all attributes of "Talent class".

Like Javascript, the programming language which use prototype based object model, there is no clear distinction between class and instance. They are just an object. In those language, there is the object which can be a prototype. This prototype object become a template of other objects. Any object can be a prototype of other object in run-time or compile time. Objects which are created by prototype, they are share properties of prototype.

When you create object in javascript, you don't need to define class separately like we do in java. Instead of that, you can define attributes of object using "constructor function". Any javascript function can be a constructor function, you can create new object using constructor function with "new" keyword

Class-based
Prototype-based
Distinguish class and instance
All object can be inherited from other object
Need explicit class definition. Instance can be created by constructor.
Object definition and creation can be possible using "constructor function"
Create one object with new keyword
same
Define inheritance structure like subclass using class definition.
Define inheritance structure by assign prototype object with constructor function
Not possible to add attribute in runtime.
Possible to add/remove attribute in runtime


I'll use following inheritance structure as an example.



1. Inheritance

Let's see Painter object which inherit from Talent object. As see in [line 10], Talent.call(this) method is called in Painter object. About call function, look at this. You can call the "construct function" of Talent with"this" and additional arguments. Then, as see in [line 13], Talent.prototype is assigned to Painter.prototype. The prototype property is a special attribute in javascript, all function have this attribute. (Just for your reference, the function is declared like  function finctName {...} but not assigned to attribute of some object. The method is a function which is assigned to attribute of object.)

Let's see javascript internal object creation procedure. 

var psy = new Singer;

If there is new keyword, create generic object and make it as the value of this keyword in Signer's constructor function. Then, the "majorTalent" attribute is assigned explicitly in constructor function. In that order, the __proto__ attribute is assigned to the prototype attribute of Singer's constructor function implicitly.(The __proto__ attribute determine prototype which contains attribute to be returned)

When javascript read some attribute, javascript check the attribute in object first. If the attribute is not exist in object, javascript check prototype chain using __proto_ attribute. If the object in prototype chain have the attribute, javascript return it. If the property is not exist in prototype chain, javascript say that the attribute is not exist.


2. More flexible object creation - constructor function with arguments
Make constructor function can receive arguments.


3. More flexible object creation - default value
As see in [line4 ~ line6], you can set default value using "||" operator. If there is a value, it is used. But if there is no value, empty value is used as a default. The constructor function of Talent object is assigned to base attribute like [line 11]. The base attribute is not a special attribute. Any name is possible. Then, call constructor function in [line 12]

4. why we should assign "prototype" attribute explicitly?
Let's see TalentedForMusic and TalentedForPaint closely. The prototype attribute of TalentedForMusic is not assigned as its parent's prototype. Look at [line 43 - 46]. If you add additional attribute of Talent.prototype, the hummingMan object which instance of TalentedForMusic don't have this attribute. i.e. The attribute is not added since TalentedForMusic don't have parent's prototype attribute.

5. "The property in constructor function" vs "object.prototype.property = vlaue"
Let's see "name" attribute and "grade" attribute closely. The "name" attribute is declared in constructor function, the "grade" attribute is declared as prototype property like "Talent.prototype.grade = "none". This is an important difference. As see in [line32 - 43], if you assign new value to name attribute, values of instances are not changed. On the other hand, if it declared as a prototype attribute like [line 46 - 48], values of its instances will be changed as new value is assigned.

6. instanceof, __proto__
As see in [line 38], you can find the class using instanceof keyword. All javascript object have __proto__ attribute except Object.(All javascript function have prototype attribute). When object is created, the prototype attribute of constructor function is assigned to __proto__ attribute. As see in [line 39 - 41], the __proto_ property of object is same with the property of constructor function.

7. Use global value in constructor function
You have to be careful when use global variable in constructor function. You might be expected the value of count 1 in [line 35], but it is 3 since it called [line32], [line29], [line17]. i.e. it is also called when assign prototype attribute.


8. Javascript doesn't support multiple inheritance.
You might assume that TalentForMusic constructor function crates two object Talent and Personality. Actually, it is possible to use of attributes of Personality from Personality. But as see in [line 35], if you add new attribute to Personality, this attribute is not added to TalentedForMusic.


9. Reference
- MDN - https://developer.mozilla.org/en/docs/Web/JavaScript
- Jasmine - http://jasmine.github.io/2.3/introduction.html