Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Presented at RejectJS Berlin 2012

Slide 3

Slide 3 text

Ich bin • Adam Hawkins • tw://adman65 • adn://twinturbo • gh://twinturbo • http://broadcastingadam.com

Slide 4

Slide 4 text

Should I listen to this guy?

Slide 5

Slide 5 text

You’re god damn right

Slide 6

Slide 6 text

Who, What, Why?

Slide 7

Slide 7 text

What is the Holy Grail? • Browser independent • All tests written in JavaScript • Unit and integration support • Test against a single browser in development • Test against all target browsers in CI • Runs from the CLI--not by opening some random HTML page.

Slide 8

Slide 8 text

Why Care? • Testing + Multi browser CI is the only way to know that your application is functioning correctly. • Manually test if you want....

Slide 9

Slide 9 text

Where are We Now? • Individual aspects, but nothing working in concert • Classic choose any 3: cheap, fast, scalable • Choose one implementation and lose one of the three

Slide 10

Slide 10 text

How did We Get Here? • Javascript not really considered a first class language until recently • Javascript traditionally confined to the browser • Focus on Single Page Applications/Client side JS MVC/Insert your term here

Slide 11

Slide 11 text

Learning to test my javascript app What have you been doing all these years?

Slide 12

Slide 12 text

This is harder than Haskell

Slide 13

Slide 13 text

Let’s See What’s Out There

Slide 14

Slide 14 text

*this is for testing Javascript applications

Slide 15

Slide 15 text

PhantomJS

Slide 16

Slide 16 text

$ phantomjs phantomjs> console.log("Yo Dawg"); Yo Dawg undefined phantomjs>

Slide 17

Slide 17 text

var page = require('webpage').create(); var url = 'http://www.phantomjs.org/'; page.open(url, function (status) { page.includeJs("jquery.min.js", function() { // jQuery is loaded, now manipulate the DOM }); page.evalute(function() { // evaluate code in the remote context $("title").html("ZOMG"); }); phantom.exit(); });

Slide 18

Slide 18 text

• Headless browser • Execute JavaScript programmatically in a real content • Real DOM

Slide 19

Slide 19 text

CasperJS

Slide 20

Slide 20 text

casper.start "http://www.google.fr/", -> @test.assertTitle "Google", "google homepage title incorrect" @test.assertExists 'form[action="/search"]', "main form is found" @fill 'form[action="/search"]', q: "foo", true casper.then -> @test.assertTitle "foo - Recherche Google", "google title is ok" @test.assertUrlMatch /q=foo/, "search term has been submitted" @test.assertEval (-> __utils__.findAll("h3.r").length >= 10 ), "google search for \"foo\" retrieves 10 or more results" casper.run -> @test.renderResults true

Slide 21

Slide 21 text

• Built on top of PhantomJS • Used to write integration tests • DOM interaction API. Example: fillIn(“foo”, “bar”) • Should not be used to write unit tests • Good for a small # of test cases • Tests bound PhantomJS

Slide 22

Slide 22 text

JSTestDriver Made by:

Slide 23

Slide 23 text

GreeterTest = TestCase("GreeterTest"); GreeterTest.prototype.testGreet = function() { var greeter = new myapp.Greeter(); assertEquals("Hello World!", greeter.greet("World")); };

Slide 24

Slide 24 text

JSTestDriver Architecture

Slide 25

Slide 25 text

• Runs from the command line • Supports multiple browsers • Unclear how to do integration tests • Browser independent, but test runner dependent • Must maintain browser installations • Tests bound to test runner

Slide 26

Slide 26 text

ZombieJS

Slide 27

Slide 27 text

// Load the page from localhost browser = new Browser() browser.visit("http://localhost:3000/", function () { // Fill email, password and submit form browser. fill("email", "[email protected]"). fill("password", "eat-the-living"). pressButton("Sign Me Up!", function() { // Form submitted, new page loaded. assert.ok(browser.success); assert.equal(browser.text("title"), "BRAINZ"); }); });

Slide 28

Slide 28 text

• Clear support for integration tests • Decent DOM interaction API • Built on Node • Tests bound to ZombieJS • Similar to CasperJS + PhantomJS

Slide 29

Slide 29 text

Capybara

Slide 30

Slide 30 text

# rspec describe "the signup process", :type => :request do it "signs me in" do # capybara within("#session") do fill_in 'Login', :with => '[email protected]' fill_in 'Password', :with => 'password' end click_link 'Sign in' end end

Slide 31

Slide 31 text

• Multiple adapters for headless or real browsers • Easy to maintain large test suites • Write tests in Ruby • Test code and app code exist in separate threads. This can be confusing.

Slide 32

Slide 32 text

What do these have in Common?

Slide 33

Slide 33 text

• No unified framework for unit tests and integration tests • Dependency hell (c++, python, node, java, or ruby) • Tests bound to test runner • Not all methods CLI friendly

Slide 34

Slide 34 text

Approaching the Holy Grail

Slide 35

Slide 35 text

How can we combine these things?

Slide 36

Slide 36 text

1. Remove test runner dependence 2. Unify integration and unit tests 3. Run in a headless browser for local tests 4. Run in real browsers in CI 5. Try to do it with Javascript

Slide 37

Slide 37 text

Step 1: Writing Tests

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

// this is a unit test test("hello test", function() { ok(1 == "1", "Passed!"); }); // this is an integration test test("user can fill in the form", function() { fillIn("#todo", "Fix JS Testing"); press("#todo-form submit"); assertContains("#todos", "Fix JS Testing"); });

Slide 40

Slide 40 text

* Use jQuery to implement a higher level DOM interaction UI similar to capybara for tests

Slide 41

Slide 41 text

Step 2: Run The Tests

Slide 42

Slide 42 text

QUnit Example

Slide 43

Slide 43 text

# Use phantomJS to open the HTML page # and capture the results $ phantomjs run_qunit.js tests.html ..................... 10 Tests, 10 Passes, 0 Failures

Slide 44

Slide 44 text

Step 3: Multiple Browsers

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

Integration Tests

Slide 47

Slide 47 text

# Upload that HTML file to your CI server # so it can be accessed with test swarm $ swarm execute tests.js Running in all browsers.... IE9: 10 tests, 10 Passed, 0 Failures IE6, 10 tests, 0 Passed, 12349871234987 Failures Chrome: 10 tests, 10 Passed, 0 Failures FireFox: 10 tests, 10 Passed, 0 Failures

Slide 48

Slide 48 text

# ENV variables determine build # Ideally set in your CI process $ swarm execute tests.js BROWSER=IE9 $ swarm execute tests.js BROWSER=FF,SAFARI,CHROME

Slide 49

Slide 49 text

Is this Better?

Slide 50

Slide 50 text

You’re god damn right

Slide 51

Slide 51 text

Why? • Completely browser independent • All tests javascript • Test runner independent • CI Friendly • Tests and app code running in the same thread. Read: direct access to app + ui.

Slide 52

Slide 52 text

The community can solve this problem

Slide 53

Slide 53 text

I’ve Already Started • https://github.com/radiumsoftware/iridium

Slide 54

Slide 54 text

Bitte warten Sie

Slide 55

Slide 55 text

TL;DR: There is no Holy Grail

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Why? • Requirements change test system • Testing backend + frontend requires a different approach • The holy grail for that is use testing tools for what language your backend is in • Things like HTML5 Audio/Video can induce complexity

Slide 58

Slide 58 text

Moral of the Story • Testing Javascript applications is difficult but possible • Decide if you want to test your frontend and backend together. • Oh ya....test that shit!

Slide 59

Slide 59 text

Die Slides https://speakerdeck.com/u/twinturbo