Client-side Testing with Jasmine, CasperJS, and JsTestDriver
Slides from a 15-minute talk given to the NYC Selenium Meetup about how we do Client-side testing at Intent Media currently and what next-generation technologies we're looking at.
a large, complicated Javascript codebase that needs testing We want to ensure our codebase works in all major browsers, back to IE 7 We want the experience of reading, writing, and running tests to be as fast and easy as possible 2. Remote Dependency Testing We place sophisticated, tightly integrated ads on sites that don’t belong to us We have dependencies on DOM elements, meta tags, cookies, etc We want to ensure we don’t break our partner sites and vice versa
CoffeeScript) Test runner: JsTestDriver (+ Ant) CI server: TeamCity We use a captured Firefox process for local & TeamCity testing 2. Remote Dependency Testing CasperJS + our own custom library (to be open-sourced soon) Runs every hour on TeamCity Hourly screenshots of our ads are visible at a private URL and on our kitchen dashboard
1000 tests total Takes 60 seconds to run locally using JsTestDriver and Firefox (assuming CoffeeScript files are already compiled) Every branch pushed to Github runs through our Jasmine tests along with all unit/integration tests (RSpec, JUnit, Cucumber) 2. Jasmine Code Style CoffeeScript, while not perfect, means less boilerplate We use custom aliases of the “describe” keyword to make logical structure of units more explicit and human-readable and to expose code smells beforeEach steps should map to “context” (or “say”) steps Text
subject = IntentMedia.Date.convert_YYYYMMDD_to_MonDDYYYY say "the date is formatted correctly", -‐> it "should handle double-‐digit months and days", -‐> expect(subject('20121015')).toBe 'Oct 15, 2012' it "should handle single-‐digit months and days", -‐> expect(subject('20120105')).toBe 'Jan 5, 2012' it "should handle double-‐digit years", -‐> expect(subject('990201')).toBe 'Feb 1, 1999' say "the date is falsey", -‐> it "should return an empty string", -‐> expect(subject(null)).toBe '' expect(subject('')).toBe ''
wakeUp(); } } describe "#wakeUpInTheMorning", -‐> subject = wakeUpInTheMorning beforeEach -‐> spyOn('wakeUp') say "it is morning", -‐> beforeEach -‐> spyOn('isMorning').andReturn true it "should call wakeUp", -‐> subject() expect(wakeUp).toHaveBeenCalled() say "it is not morning", -‐> beforeEach -‐> spyOn('isMorning').andReturn false it "should not call wakeUp", -‐> subject() expect(wakeUp).not.toHaveBeenCalled() Not every function is worth testing
screenshots of a page or a particular DOM element Can execute arbitrary Javascript or load external JS into the remote window context Assert selector visibility, HTTP status, redirection Easy to write an even more terse library on top, which we did
the ability to run/test real JS in real browsers Before that we were testing our JS in JUnit using an emulated browser environment, and it was not good Every modern browser can be captured, even IE6 in a VM can run tests (though easy to get into a bad state) 2. Cons Stability issues, could be faster Dependency on Java Browser start/capture/stop process is difficult to automate No native support for watching files Limited configuration options
powerful configuration options Handles the tedium of browser launching & capturing Handles the tedium of pre/post-processing files (coffeescript compile, jslint, etc - we use Ant to do that right now) github.com/airportyh/testem
with JsTestDriver) to test Angular.js apps Uses Node.js and socket.io, faster and more stable than JsTestDriver’s heartbeat implementation (though it sounds like IE8 has speed issues) Can watch files and re-run on file change (like Guard) Demo video shows a 1,535 Jasmine test run taking 3 seconds Is called “Testacular” github.com/vojtajina/testacular
automate testing on dozens of browser/OS combinations, including IE 6-10 on Windows XP, 7 and 8 This is basically the Holy Grail of client-side testing, many projects are working on BrowserStack integration or something similar (e.g. Bunyip, Buster.JS, Testling, Testacular, TestSwarm) Supports a command line interface and localhost tunneling which could point to a locally-running test runner
browser opening/capturing/closing The test runner owns file watching + auto-run “Browsers in the cloud” quickly becoming a reality (BrowserStack, Testling, AWS) Faster and more stable = a better dev/test experience Better debugging and IDE integration (see Testacular demo) Written for large-scale JS apps (Angular.js, Ember.js, Backbone, etc)