Automated javascript unit testing

Preview:

Citation preview

Automated Javascript Unit Testing

Ryan Chambers

ryan.chambers@intelliware.ca

Intelliware Development

Outline

• Why should I write javascript tests?

• Testing goals

• The code we’re going to test

• How can I automate the tests?– JS Test Driver– QUnit and Selenium– Jasmine Maven Plugin– QUnit, PhantomJS and JS Test Runner

Testing Goals

1. Easy to develop tests in eclipse

2. Tests runnable as part of maven build

3. Fast

4. Cross-browser

Why write javascript unit tests?

• Why write java tests?

• Faster to test unit tests than to browse actual application

• Can help find new bugs

• Easy to add to part of build process, giving you another layer of testing

Code to be tested

var validation = function() {… return { validateAlphaNumeric: function(field) {..}, validateUsername : function (field) { $.ajax({…}); } }}

JS Test Driver• Project from google to automate javascript testing• Provides a javascript unit testing framework• Maven plug-in and eclipse plug-in• Uses real browsers

– However, I had problems with Chrome– Browsers weren’t always closed after

• No need to create fixture html – but you can specify html in each test

• Server isn’t needed unless for AJAX• Has a QUnit adapter• Some flakiness, nothing I could reproduce reliably but I felt like

it did weird stuff sometimes• http://code.google.com/p/js-test-driver/

Test CodeTestCase("validateAlphaNumeric", {

"test validateAlphaNumeric valid" : function() { /*:DOC input = <input type="text" id="test_field" value="aB1" /> */

var field = $(this.input),isValid;

isValid = validation.validateAlphaNumeric(field);assertEquals("aB1 should be valid", true, isValid);

},"test validateAlphaNumeric invalid" : function() {

/*:DOC input = <input type="text" id="test_field" value="!" /> */var field = $(this.input),

isValid;isValid = validation.validateAlphaNumeric(field);assertEquals("! is not valid alphanumeric value", false,

isValid);}

});

Sinon.JS

• A library for standalone test spies, stubs and mocks

• Can also fake XHR (AJAX) and servers

• http://sinonjs.org/

Testing AJAX with SinonJSTestCase("validateUsername", sinon.testCase({

setUp : function() {validation.reset();this.server = sinon.fakeServerWithClock.create();

/*:DOC input = <input type="text" id="test_field" value="!" /> */

},tearDown: function() { this.server.restore();},"test validateUsername user name already used" : function() {

var field = $(this.input), isValid;this.server.respondWith(createFakeResponse('error : username

already used'));isValid = callValidation(field, this.server);assertEquals("should have got username already used error",

isValid, false);}

}

Testing AJAX with SinonJSfunction createFakeResponse(responseCode) { return [ 200, { "Content-Type": "text/xml" }, '<?xml version="1.0" encoding="UTF-8"?>

<result>' + responseCode + '</result>'];

}

function callValidation(field, server) {validation.validateUsername(field);server.respond();return validation.validateAlphaNumeric(field);

}

JS Test Runner Demo

• Demo of eclipse plug-in• Demo of maven build

https://github.com/ryan-chambers/jstestdriver-sample-project

QUnit and Selenium

• QUnit is a javascript testing library originally written to test jQuery

• Selenium tests using real browsers– very slow– Typically only one browser is tested

• Meant for automated testing of page flows, not javascript unit testing

• I wrote a sample test that only records whether all tests pass or not– could possibly be updated to record actual error

• Requires creating an html page to host testshttps://github.com/jquery/qunit

QUnit Test page

• Includes all required css and javascript

• Adds elements to DOM that are required for test

<input type="text" id="test_field" value="!" />

QUnit Tests - setup

module("validation", {setup : function() {

this.field = $('#test_field'); validation.reset();

},tearDown: function () {

this.xhr.restore(); }});

QUnit Tests

test("validateAlphaNumeric invalid", function() {

this.field.val('!');var isValid =

validation.validateAlphaNumeric(this.field);

equal(isValid, false, "! Shouldnot be a valid alphanumericvalue");

});

QUnit Tests with SinonJSfunction testFakeUserValidationWithResponse(responseCode,

testHandler) {var server = sinon.fakeServerWithClock.create();try {

server.respondWith([ 200, { "Content-Type": "text/xml" }, '<?xml version="1.0" encoding="UTF-

8"?><result>‘ + responseCode + '</ result >' ]);

testHandler(server); } finally { server.restore(); }}

QUnit tests with SinonJStest("validateUsername user name already used", function(

{var that = this;testFakeUserValidationWithResponse(username_already_used', function(server) {

validation.validateUsername(that.field);

server.respond();

var isUsernameValid = validation.isUsernameValid(); equal(isUsernameValid, false, "should have got

username already used error");});

});

Seleniumpublic class ValidationIntegrationTest {

private WebDriver driver;

@Beforepublic void setUp() throws Exception {

driver = new FirefoxDriver();}

@Testpublic void testFormsIntegration() throws Exception {

driver.get("http://localhost:8080/validation.html");assertPageIsLoaded();List<WebElement> failedElements = driver.findElements(By.xpath("//span[@class='fail']"));// could do more here to return failure messagesAssert.assertEquals(0, failedElements.size());

}

private void assertPageIsLoaded() {driver.findElement(By.id("qunit-header"));

}

@Afterpublic void tearDown() throws Exception {

driver.close();}

}

QUnit demo

• View page in browser• Run unit test in eclipse w/ Jetty

https://github.com/ryan-chambers/qunit-selenium-sample-project

Jasmine Maven Plug-in

• Jasmine is a BDD javascript unit test framework– You create a “spec” in a DSL that almost looks like English– BDD is very popular in Ruby on Rails

• https://github.com/pivotal/jasmine/wiki

• Jasmine maven plug-in looks in certain directories for tests and their specs– Can configure includes for libraries (eg. Jasmine/sinon plug-in)– Uses HTMLUnit for DOM

• https://github.com/searls/jasmine-maven-plugin

Jasmine Specsdescribe('validation', function() { it('should detect valid alphanumeric sequences',

function() {var isValidAlphaNumeric =

validation.isAlphaNumeric('aA1');expect(isValidAlphaNumeric).toEqual(true);

});

it('should detect invalid alphanumeric sequences', function() {

var isValidAlphaNumeric = validation.isAlphaNumeric('ab@google.com');

expect(isValidAlphaNumeric).toEqual(false); });});

Jasmine Maven Plug-in demo

• Run tests in browser– mvn jasmine:bdd– http://localhost:8234

• Run maven build

https://github.com/ryan-chambers/maven-jasmine-sample-project

QUnit, PhantomJS and JS Test Runner

• PhantomJS is a command-line tool that embeds WebKit and can run javascript– Very fast, since there is no browser

http://www.phantomjs.org/

• JS Test Runner is a JUnit test runner that uses QUnit and Phantomjs for javascript– Runnable from maven and eclipse

http://js-testrunner.codehaus.org/

JS Test Runner

@RunWith(JSTestSuiteRunner.class)

@JSTestSuiteRunner.Include(value="validation.html")

public class ValidationJSTest {

}

QUnit, PhantomJS and JS Test Runner

• Run unit tests from Eclipse• Run build from maven

https://github.com/ryan-chambers/jstest-runner-sample-project

Questions?

Recommended