Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Javascript Testing: The Holy Grail

Javascript Testing: The Holy Grail

Approaching the Holy Grail of Javascript application testing. Presented at RejectJS in Berlin

Adam Hawkins

October 04, 2012
Tweet

More Decks by Adam Hawkins

Other Decks in Programming

Transcript

  1. Ich bin • Adam Hawkins • tw://adman65 • adn://twinturbo •

    gh://twinturbo • http://broadcastingadam.com
  2. 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.
  3. Why Care? • Testing + Multi browser CI is the

    only way to know that your application is functioning correctly. • Manually test if you want....
  4. 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
  5. 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
  6. 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(); });
  7. 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
  8. • 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
  9. GreeterTest = TestCase("GreeterTest"); GreeterTest.prototype.testGreet = function() { var greeter =

    new myapp.Greeter(); assertEquals("Hello World!", greeter.greet("World")); };
  10. • 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
  11. // 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"); }); });
  12. • Clear support for integration tests • Decent DOM interaction

    API • Built on Node • Tests bound to ZombieJS • Similar to CasperJS + PhantomJS
  13. # 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
  14. • 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.
  15. • 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
  16. 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
  17. // 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"); });
  18. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>QUnit Example</title> <link rel="stylesheet"

    href="/resources/qunit.css"> </head> <body> <div id="qunit"></div> <script src="apps.js"></script> <script src="tests.js"></script> </body> </html>
  19. # Use phantomJS to open the HTML page # and

    capture the results $ phantomjs run_qunit.js tests.html ..................... 10 Tests, 10 Passes, 0 Failures
  20. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Integration Tests</title> <link rel="stylesheet"

    href="/resources/qunit.css"> </head> <body> <div id="qunit"></div> <script src="apps.js"></script> <script src="tests.js"></script> <!-- Added this to hook into test swarm --> <script src="http://example.org/testswarm/js/inject.js"></script> </body> </html>
  21. # 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
  22. # ENV variables determine build # Ideally set in your

    CI process $ swarm execute tests.js BROWSER=IE9 $ swarm execute tests.js BROWSER=FF,SAFARI,CHROME
  23. 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.
  24. 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
  25. 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!