testing of javacript
TRANSCRIPT
Testing Javascript
Lei Kang @ Kiwiplan
Do you want...
Unit Testing of JavaScript code?
Continuous Integration?
Automated Testing for JavaScript intensive Web App?
Are those your itches for Web Development?
Every good work of software starts by scratching a developer’s personal itch.
-- The Cathedral and the Bazaarby Eric S. Raymond
My itches of JavaScript
Object Oriented Javascript
Javascript with Ruby’s flavor
Distributed Event Registering and Triggering
Reflection of Javascript
Unit Testing
Continuous Integration for Javascript Project
Automated Web UI testing
Today’s Story
Unit Testing using Rhino and RhinoUnit
Continuous Integration using Jenkins(Hudson)
Automated Web UI testing using iMacros
Rhino?
Rhino isAn open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.
http://www.mozilla.org/rhino/
Rhino now
The Mozilla Rhino engine for the JavaScript programming language, is currently included as a feature in the JDK 6 and JRE 6 libraries.
Rhino can
Run JavaScript inside Java
ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine jsEngine = mgr.getEngineByName("JavaScript"); try { jsEngine.eval("print('Hello, world!')"); } catch (ScriptException ex) { ex.printStackTrace(); }
Rhino can
Run Java inside JavaScript
importPackage(javax.swing); var optionPane = JOptionPane.showMessageDialog(null, 'Hello, world!');
Rhino can Run in the Terminal: Linux, Windows and MacOS X: jrunscript
$ sudo apt-get install rhino; js
already feeling impatient?
Here comes RhinoUnit
An Ant based Javascript testing framework
RhinoUnit is run from an ANT scriptdef task using the Rhino engine - and uses all the helpful things that ANT provides for that.
What RhinoUnit can doAlmost the same APIs with JUnit
• string and object comparisons
• regexp comparisons
• collection comparisons (contains, containsExactly, etc)
• ensure that a function has been called (by wrapping it with assert.mustCall(), or using an assert.functionThatMustBeCalled()).
• ensure that an exception is thrown (using shouldThrowException(...)
• ensure that the global namespace isn't polluted by poor variable scoping. (I will talk about it later)
A general example
/** * Number.times function. 3.times(function(item){...}) * * @param handler function * @returns {Array} */Number.prototype.times = function(handler) {
var results = new Array();if (this > 0 && this == parseInt(this)) {
for ( var i = 0; i < Math.ceil(this); i++) {results.push(handler(i));
}} else {
throw "Illegal number for times() function. Positive integer is required.";}return results;
};
we have a simple function like this
A general example
eval(loadFile("src/com/ciphor/ruby/Number.js"));var testNumber;
testCases(test,
function setUp(){testNumber = 12;
},
function testNumberTimes(){var aNumber = new Number(10);var temp = 0;var result = aNumber.times(function(item){
temp++;return aNumber*item;
});assert.that(temp, eq(10));assert.that(result, isCollectionContainingOnly(0,10,20,30,40,50,60,70,80,90));
});
Test code goes like this
Our Ant build.xml<project name="CiphorJS" basedir="." default="run-unit-tests">
<scriptdef name="rhinounit" src="lib/rhinounit/src/rhinoUnitAnt.js" language="javascript">
<attribute name="options"/><attribute name="ignoredglobalvars"/><attribute name="haltOnFirstFailure"/><attribute name="rhinoUnitUtilPath"/><element name="fileset" type="fileset"/>
</scriptdef>
<target name="run-unit-tests"><rhinounit options="{verbose:true, stackTrace:true}"
haltOnFirstFailure="false" rhinoUnitUtilPath="lib/rhinounit/src/rhinoUnitUtil.js">
<fileset dir="test"><include name="Test*.js"/>
</fileset></rhinounit>
</target></project>
I am sure you know how to do the next
familiar with this?
run-unit-tests:[rhinounit] Testsuite: TestCore.js[rhinounit] *** Empty TestCase, unavailable for OOAD module. ***[rhinounit] Tests run: 1, Failures: 0, Errors: 0[rhinounit] Testsuite: TestEve.js[rhinounit] Tests run: 4, Failures: 0, Errors: 0[rhinounit] Testsuite: TestReflection.js[rhinounit] Tests run: 5, Failures: 0, Errors: 0[rhinounit] Testsuite: TestRubyArray.js[rhinounit] Tests run: 26, Failures: 0, Errors: 0[rhinounit] Testsuite: TestRubyNumber.js[rhinounit] Tests run: 3, Failures: 0, Errors: 0[rhinounit] Testsuite: TestRubyOO.js[rhinounit] Tests run: 7, Failures: 0, Errors: 0[rhinounit] Testsuite: TestRubyString.js[rhinounit] Tests run: 12, Failures: 0, Errors: 0BUILD SUCCESSFULTotal time: 922 milliseconds
want to see more?
assert.that(actual, predicate)assert.mustCall(onThisObject, thisMethod)assert.mustCallNTimes(onThisObject, numberOfTimes, thisMethod)assert.functionThatMustBeCalled(thisMethod, originalFunction)assert.functionThatMustBeCalledNTimes(thisMethod, numberOfTimes, originalFunction)assert.mustNotCall(onThisObject, thisMethod)assert.functionThatMustNotBeCalled(thisMethod)assert.fail(message)assert.callStack(optionalIgnoreAfterMatching)
eq(expected)similar(expected)matches(regExp)isTrue(message)isFalse(message)not(predicate)hasConstructor(expected)isA(expected)isOfType(expected)isCollectionContaining(value, value, value...)isCollectionContainingOnly(value, value, value...)containsInOrder(value, value, value...)isNull(message)eqFloat(expected, accuracy)shouldThrowException(theTest, message, checkException)
Assert Object
Test Functions
http://code.google.com/p/rhinounit/
Test Functions
Continuous Integration
Why do we need CI?
Can we use CI for Javascript Project?
Which CI system are we going to use?
How to do it?
Jenkins
jenkins-ci.org
JenkinsJenkins
Jenkins
Formerly known as Hudson
Free to use (applause!)
Written in Java
Distributed by jenkins.war (Easy to deploy)
Native package for ubuntu
How I use it
A Javascript Project in Eclipse
Using git for version controlling, you need a git plugin for eclipse
Source code hosted on GitHub (github.com)
Unit tested by RhinoUnit (Ant)
Interested in Git?
Let Jenkins working with githubInstall Github plugin
Install Git plugin
Let Jenkins working with github
Create a new job for your project
Let Jenkins working with github
Set Ant Target to run
Let Jenkins working with github
Set Git Publisher
Optionally push merge results, tags, and/or branches to remote repositories.
Let Jenkins working with github
Generate SSH key and add it to GitHub
Set Git autentication info in Jenkins workspace
$ git config user.name “your.name”$ git config user.email “[email protected]”
Hooray!
10:24:59 Started by user anonymous10:24:59 Checkout:workspace / /var/lib/jenkins/jobs/CiphorJS/workspace - hudson.remoting.LocalChannel@1077aa710:24:59 Using strategy: Default10:24:59 Last Built Revision: Revision 711a1c557c248b5e45364d3aafd8a4c98031f8a1 (local/master)10:24:59 Checkout:workspace / /var/lib/jenkins/jobs/CiphorJS/workspace - hudson.remoting.LocalChannel@1077aa710:24:59 GitAPI created10:24:59 Fetching changes from the remote Git repository10:24:59 Fetching upstream changes from /home/lei.kang/workspaces/java/CiphorJS/.git10:24:59 [workspace] $ git fetch -t /home/lei.kang/workspaces/java/CiphorJS/.git +refs/heads/*:refs/remotes/local/*10:25:00 [workspace] $ git ls-tree HEAD10:25:00 Fetching upstream changes from [email protected]:kangleay/CiphorJS.git10:25:00 [workspace] $ git fetch -t [email protected]:kangleay/CiphorJS.git +refs/heads/*:refs/remotes/origin/*10:25:13 [workspace] $ git ls-tree HEAD10:25:13 Seen branch in repository local/master10:25:13 Seen branch in repository origin/master10:25:13 Commencing build of Revision 711a1c557c248b5e45364d3aafd8a4c98031f8a1 (local/master)10:25:13 GitAPI created10:25:13 Checking out Revision 711a1c557c248b5e45364d3aafd8a4c98031f8a1 (local/master)10:25:13 [workspace] $ git checkout -f 711a1c557c248b5e45364d3aafd8a4c98031f8a110:25:13 [workspace] $ git tag -a -f -m "Hudson Build #37" hudson-CiphorJS-3710:25:13 Recording changes in branch local/master10:25:14 [workspace] $ git whatchanged --no-abbrev -M –pretty=raw 711a1c557c248b5e45364d3aafd8a4c98031f8a1..711a1c557c248b5e45364d3aafd8a4c98031f8a1
What is it doing behind?
What is it doing behind?10:25:14 [workspace] $ ant -file build.xml run-unit-tests10:25:14 Buildfile: build.xml10:25:15 10:25:15 run-unit-tests:10:25:15 [rhinounit] Testsuite: TestCore.js10:25:15 [rhinounit] *** Empty TestCase, unavailable for OOAD module. ***10:25:15 [rhinounit] Tests run: 1, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestEve.js10:25:15 [rhinounit] Tests run: 4, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestReflection.js10:25:15 [rhinounit] Tests run: 5, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestRubyArray.js10:25:15 [rhinounit] Tests run: 26, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestRubyNumber.js10:25:15 [rhinounit] Tests run: 3, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestRubyOO.js10:25:15 [rhinounit] Tests run: 7, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 [rhinounit] Testsuite: TestRubyString.js10:25:15 [rhinounit] Tests run: 12, Failures: 0, Errors: 010:25:15 [rhinounit] 10:25:15 10:25:15 BUILD SUCCESSFUL10:25:15 Total time: 0 seconds10:25:15 GitAPI created10:25:15 [workspace] $ git tag -d hudson-CiphorJS-3710:25:15 [workspace] $ git tag -a -f -m "Hudson Build #37" hudson-CiphorJS-37-SUCCESS10:25:15 GitAPI created10:25:15 Pushing HEAD to branch master at repo origin10:25:15 [workspace] $ git push [email protected]:kangleay/CiphorJS.git HEAD:master
10:25:20 Finished: SUCCESS
What have I done?
Commit Git Local Repository Detected by
Build and TestJenkins
workspace
Git Remote Repository(GitHub)
PublishSuccess
As an open source project, people now can check out source from github and enjoy!
Extremely bored?Should I continue?
Automated Web UI Testing
A lot of options
Selenium, Tellurium...
For simplicity, iMacros
iMacros
Just DRYDon't Repeat Yourself
iMacros
Works for both IE and Firefox
iMacros
Let's do itActions speak louder than words.
Web Testing
Web Testing
Web Testing
Web Testing
Web Testing
OK, That's the whole story today, Thank you!
Reference
Rhino http://www.mozilla.org/rhino/
RhinoUnit http://code.google.com/p/rhinounit/
Jenkins http://jenkins-ci.org/
iMacros https://addons.mozilla.org/en-us/firefox/addon/imacros-for-firefox/
CiphorJS http://www.ciphor.com/wiki/index.php/CiphorJS
Coming soon...
Javascript with Ruby flavor
• Ruby Array object
• Ruby Number object
• Ruby String object
• ...
Reflection of Javascript
Event Listening Extension
CiphorJS project
Object oriented way of writing javascript
• Inheritance
• Package importing
• Interface
• ...