Running JavaScript Tests with Maven, Jasmine and PhantomJS

May 4th, 2014 by

Sometimes in a project there is the need to run tests for your client-side code, written in JavaScript from a Maven build.
One reason might be that Maven manages a complex build life-cycle in your project and you need a close integration for your JavaScript tests, another one might be that you’re in an environment where it is complicated to install and manage additional software like an integration- or build-server.

A Javascript Class to be tested

A Javascript Class to be tested

 

Maven Dependencies

We only need two dependencies here for the Jasmine Maven Plug-in and the PhantomJS Maven Plug-in. To use them, we’re adding the following lines to our pom.xml:

<build>
	<plugins>
		<plugin>
			<groupId>com.github.klieber</groupId>
			<artifactId>phantomjs-maven-plugin</artifactId>
			<version>0.2.1</version>
			<executions>
				<execution>
					<goals>
						<goal>install</goal>
					</goals>
				</execution>
			</executions>
			<configuration>
				<version>1.9.2</version>
			</configuration>
		</plugin>
		<plugin>
			<groupId>com.github.searls</groupId>
			<artifactId>jasmine-maven-plugin</artifactId>
			<version>1.3.1.4</version>
			<executions>
				<execution>
					<goals>
						<goal>test</goal>
					</goals>
				</execution>
			</executions>
			<configuration>
				<jsSrcDir>src/main/webapp/js/app</jsSrcDir>
				<jsTestSrcDir>src/test/webapp/js/specs</jsTestSrcDir>
				<preloadSources>
					<source>src/main/webapp/js/app/GreetingService.js</source>
				</preloadSources>
				<webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
				<webDriverCapabilities>
					<capability>
						<name>phantomjs.binary.path</name>
						<value>${phantomjs.binary}</value>
					</capability>
				</webDriverCapabilities>
				<haltOnFailure>true</haltOnFailure>
			</configuration>
		</plugin>
	</plugins>
</build>

JavaScript Class

This is our JavaScript client code to be tested in form of a greeting service that ..erm.. greets things with names …

function GreetingService() {
}
 
GreetingService.prototype.greeting = "Hello";
 
GreetingService.prototype.greet = function(name) {
	'use strict';
	if (!name) {
		name = "anonymous";
	}
	return this.greeting + ", " + name;
};

Jasmine Test Specs

This is our Jasmine test spec testing different possible execution paths for the GreetingService.

As I’d like to demonstrate what a failing test looks like, the third test is written to fail.

describe("the greeting service", function () {
	var greetingService;
 
	beforeEach(function(){
		greetingService = new GreetingService();
	});
 
    it("must create a valid greeting", function () {
        var greet = greetingService.greet("foo");
        expect(greet).toBe("Hello, foo");
    });
 
    it("must use an altered greeting", function(){
        greetingService.greeting = 'Hey';
        var greet = greetingService.greet("bar");
        expect(greet).toBe("Hey, bar");
    });
 
    it("must use fallback if no name given", function(){
    	var greet = greetingService.greet();
    	expect(greet).toBe("This test will be failing");
    });
});

Directory Structure

Now that we’ve written everything we need, our final directory structure should look similar to this one:

.
├── pom.xml
├── README.md
└── src
    ├── main
    │   ├── java
    │   └── webapp
    │       └── js
    │           └── app
    │               └── GreetingService.js
    └── test
        ├── java
        └── webapp
            └── js
                └── specs
                    └── greetingServiceTest.js

Running the Tests

We’re now able to run the tests using your IDE of choice with a decent Maven Builder or using Maven on the command line – and as you can see Maven downloads the phantomjs binary for your target system for you, starts webdriver using ghostdriver and evaluates the jasmine specs with the third test failing as expected:

$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maven-jasmine-phantomjs-samples 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO] --- phantomjs-maven-plugin:0.2.1:install (default) @ maven-jasmine-phantomjs-samples ---
[INFO] Downloading phantomjs binaries from https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2
[INFO] Extracting /data/project/maven-jasmine-phantomjs-tutorial/target/phantomjs-maven-plugin/phantomjs-1.9.2-linux-x86_64.tar.bz2/phantomjs-1.9.2-linux-x86_64/bin/phantomjs to /data/project/maven-jasmine-phantomjs-tutorial/target/phantomjs-maven-plugin/phantomjs-1.9.2-linux-x86_64/bin/phantomjs
[INFO]
[INFO] --- jasmine-maven-plugin:1.3.1.4:test (default) @ maven-jasmine-phantomjs-samples ---
2014-04-29 22:41:25.485:INFO:oejs.Server:jetty-8.1.14.v20131031
2014-04-29 22:41:25.505:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:56635
[INFO] Executing Jasmine Specs
Apr 29, 2014 10:41:25 PM org.openqa.selenium.phantomjs.PhantomJSDriverService
INFO: executable: /data/project/maven-jasmine-phantomjs-tutorial/target/phantomjs-maven-plugin/phantomjs-1.9.2-linux-x86_64/bin/phantomjs
Apr 29, 2014 10:41:25 PM org.openqa.selenium.phantomjs.PhantomJSDriverService
INFO: port: 15008
Apr 29, 2014 10:41:25 PM org.openqa.selenium.phantomjs.PhantomJSDriverService
INFO: arguments: [--webdriver=15008, --webdriver-logfile=/data/project/maven-jasmine-phantomjs-tutorial/phantomjsdriver.log]
Apr 29, 2014 10:41:25 PM org.openqa.selenium.phantomjs.PhantomJSDriverService
INFO: environment: {}
PhantomJS is launching GhostDriver...
[INFO  - 2014-04-29T20:41:25.673Z] GhostDriver - Main - running on port 15008
[INFO  - 2014-04-29T20:41:26.205Z] Session [a22ffc70-cfde-11e3-8869-a76f5a409c4d] - _decorateNewWindow - page.settings: {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34","webSecurityEnabled":true}
[INFO  - 2014-04-29T20:41:26.205Z] Session [a22ffc70-cfde-11e3-8869-a76f5a409c4d] - page.customHeaders:  - {}
[INFO  - 2014-04-29T20:41:26.205Z] Session [a22ffc70-cfde-11e3-8869-a76f5a409c4d] - CONSTRUCTOR - Desired Capabilities: {"javascriptEnabled":true,"phantomjs.binary.path":"/data/project/maven-jasmine-phantomjs-tutorial/target/phantomjs-maven-plugin/phantomjs-1.9.2-linux-x86_64/bin/phantomjs"}
[INFO  - 2014-04-29T20:41:26.205Z] Session [a22ffc70-cfde-11e3-8869-a76f5a409c4d] - CONSTRUCTOR - Negotiated Capabilities: {"browserName":"phantomjs","version":"1.9.2","driverName":"ghostdriver","driverVersion":"1.0.4","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}}
[INFO  - 2014-04-29T20:41:26.205Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: a22ffc70-cfde-11e3-8869-a76f5a409c4d
[INFO  - 2014-04-29T20:41:26.556Z] ShutdownReqHand - _handle - About to shutdown
[INFO]
-------------------------------------------------------
 J A S M I N E   S P E C S
-------------------------------------------------------
[INFO]
the greeting service
  must create a valid greeting
  must use an altered greeting
  must use fallback if no name given <<< FAILURE!
    * Expected 'Hello, anonymous' to be 'This test will be failing'.
 
1 failure:
 
  1.) the greeting service it must use fallback if no name given <<< FAILURE!
    * Expected 'Hello, anonymous' to be 'This test will be failing'.
 
Results: 3 specs, 1 failures
 
2014-04-29 22:41:27.081:INFO:oejsh.ContextHandler:stopped o.e.j.s.h.ContextHandler{/,file:/data/project/maven-jasmine-phantomjs-tutorial/}
2014-04-29 22:41:27.082:INFO:oejsh.ContextHandler:stopped o.e.j.s.h.ContextHandler{/spec,file:/data/project/maven-jasmine-phantomjs-tutorial/}
2014-04-29 22:41:27.082:INFO:oejsh.ContextHandler:stopped o.e.j.s.h.ContextHandler{/src,file:/data/project/maven-jasmine-phantomjs-tutorial/}
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

Alternate Download Directory for PhantomJS

In the output from the test run above, we’re able to see that the phantomjs binaries are downloaded and stored in the target directory of our project directory (“target/phantomjs-maven-plugin/phantomjs-1.9.2-linux-x86_64/bin/phantomjs“).

In some occasions, we might want to override this e.g. to avoid downloading the binaries after every mvn clean test or something similar that wipes the target directory.

We’re able to change this path by setting the phantomjs.outputDir to a directory of our choice – in the following example we’re using the system’s temp directory as a location for our binaries.

<properties>
		<phantomjs.outputDir>${java.io.tmpdir}/phantomjs</phantomjs.outputDir>
</properties>

Update: PhantomJS Maven Plugin >= 0.3

Kyle Lieber, the author of the PhantomJS Maven Plugin has released an update now pulling the binaries from the central Maven repository. This allows us to cache those files in our local Maven repository and therefore eliminates the need to specify an alternate directory as described above.

The only addition prerequisite is, that you need to use Maven >= 3.1.0.

So an updated pom.xml could look like this one:

<project>
	[..]
	<prerequisites>
		<maven>3.1.0</maven>
	</prerequisites>
 
	<build>
		<plugins>
			<plugin>
				<groupId>com.github.klieber</groupId>
				<artifactId>phantomjs-maven-plugin</artifactId>
				<version>0.3</version>
				[..]
			</plugin>
 
			[..]
		</plugins>
	</build>
 
	[..]
</project>

I’ve pushed the update to the new version in the following branch in my git repo here.

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/maven-jasmine-phantomjs-example.git

Resources

Tags: , , , , , , , , , , ,

4 Responses to “Running JavaScript Tests with Maven, Jasmine and PhantomJS”

  1. Kyle Lieber Says:

    Hi, nice quick post on using the Jasmine Maven Plugin and the PhantomJS Maven Plugin. I’m the maintainer for both of those plugins and I just wanted to let you know that I released version 0.3 of the PhantomJS Maven Plugin on May 2nd. In this version PhantomJS is now downloaded out of the Maven Central repository and cached in your local repository so you’ll no longer need to do the workaround you’ve mentioned for avoiding losing the binaries during a mvn clean.

    Also, just wanted to say I’m glad you find these plugins useful.

    Thanks,

    Kyle

  2. micha kops Says:

    Hi Kyle,

    thanks for your remark and the helpful plugin update – I’ve updated my article to reflect these changes.

    Keep up the good work!

    Micha

  3. Luca Says:

    I’m using the exact same configurations found on the official plugin site and this blog but i get this error when i run mvn test

    Unable to parse configuration of mojo com.github.searls:jasmine-maven-plugin:2.0-alpha-01:test for parameter phantomjs.binary.path: Cannot find default setter in class com.github.searls.jasmine.mojo.Capability

    Not sure why. I’m using mvn 3.2.3

  4. micha kops Says:

    Hi, I’m unable to reproduce the error described. Did you clone my repository and run mvn test?
    Perhaps the download of the artifact from the maven repository failed – you could have a look at your local maven repository at ~/.m2/repository and simply delete the directory containing the dependency posted above.

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 93,438 bad guys.

Search
Categories