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

Jumping Into Front-End Testing

Jumping Into Front-End Testing

Talking about unit, integration, and visual regression testing given at Up Front Conf in Manchester, England, 2015.

A5af583190a73a7d94255c6f79cde415?s=128

Alicia Sedlock

May 29, 2015
Tweet

Transcript

  1. Jumping Into Front-End Testing Alicia Sedlock | @aliciability Up Front,

    2015
  2. Writing tests isn’t
 glamorous.

  3. Stability it a
 responsibility.

  4. “Front-end testing”?

  5. “Front-end testing”? • Unit tests • Integration tests • Visual

    regression tests
  6. Unit tests

  7. Unit tests are for ensuring small pieces of code work

    as expected.
  8. Unit tests • Jasmine • Mocha • QUnit • Probably

    others
  9. describe(“Calculator Operations”, function () { it(“Should add two numbers”, function

    () { Calculator.init(); var result = Calculator.addNumbers(7,3); expect(result).toBe(10); }); });
  10. describe(“Calculator Operations”, function () { it(“Should add two numbers”, function

    () { Calculator.init(); var result = Calculator.addNumbers(7,3); expect(result).toBe(10); }); });
  11. describe(“Calculator Operations”, function () { it(“Should add two numbers”, function

    () { Calculator.init(); var result = Calculator.addNumbers(7,3); expect(result).toBe(10); }); });
  12. describe(“Calculator Operations”, function () { it(“Should add two numbers”, function

    () { Calculator.init(); var result = Calculator.addNumbers(7,3); expect(result).toBe(10); }); });
  13. describe(“Calculator Operations”, function () { beforeEach(function () { Calculator.init(); });

    ... afterEach(function () { Calculator.teardown(); }); });
  14. it(“Should remember the last calculation”, function () { spyOn(Calculator, “updateCurrentValue”);

    Calculator.addNumbers(7,10); expect(Calculator.updateCurrentValue).toHaveBeenCalled(); expect(Calculator.updateCurrentValue) .toHaveBeenCalledWith(17); expect(Calculator.currentValue).toBe(17); });
  15. it(“Should remember the last calculation”, function () { spyOn(Calculator, “updateCurrentValue”);

    Calculator.addNumbers(7,10); expect(Calculator.updateCurrentValue).toHaveBeenCalled(); expect(Calculator.updateCurrentValue) .toHaveBeenCalledWith(17); expect(Calculator.currentValue).toBe(17); });
  16. it(“Should remember the last calculation”, function () { spyOn(Calculator, “updateCurrentValue”);

    Calculator.addNumbers(7,10); expect(Calculator.updateCurrentValue).toHaveBeenCalled(); expect(Calculator.updateCurrentValue) .toHaveBeenCalledWith(17); expect(Calculator.currentValue).toBe(17); });
  17. it(“Should remember the last calculation”, function () { spyOn(Calculator, “updateCurrentValue”);

    Calculator.addNumbers(7,10); expect(Calculator.updateCurrentValue).toHaveBeenCalled(); expect(Calculator.updateCurrentValue) .toHaveBeenCalledWith(17); expect(Calculator.currentValue).toBe(17); });
  18. it(“Should remember the last calculation”, function () { spyOn(Calculator, “updateCurrentValue”);

    Calculator.addNumbers(7,10); expect(Calculator.updateCurrentValue).toHaveBeenCalled(); expect(Calculator.updateCurrentValue) .toHaveBeenCalledWith(17); expect(Calculator.currentValue).toBe(17); });
  19. Unit tests • Test tiny pieces of code • Validations,

    calculations, etc. • Pairs well with functional requirements
  20. Integration tests

  21. Integration tests make sure everything plays nicely.

  22. Integration tests might need additional plugins. • jasmine-integration • Karma

    • Selenium • Again, many others
  23. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Failure state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “Not An Email”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupError”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  24. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Failure state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “Not An Email”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupError”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  25. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Failure state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “Not An Email”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupError”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  26. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Failure state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “Not An Email”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupError”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  27. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Success state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “alicia@test.com”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupSuccess”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  28. describe(“Integration tests”, function () { var page; beforeEach(function (done) {

    page = visit(“/home”); page.ready(done); }); describe(“Success state”, function () { beforeEach(function (done) { page.fill_in(“input[name='email']", “alicia@test.com”); page.click(“button[type=submit]”); page.onBodyChange(done); }); it(“Shouldn’t allow invalid information”, function () { expect(page.find(“#signupSuccess”) .hasClass(“hidden”)).toBeFalsy(); }); }); });
  29. Integration tests • Do units and other pieces work together?

    • Line up well with user stories
  30. Visual regression tests

  31. Visual regression tests check for inconsistencies in the view.

  32. Before After Difference

  33. Visual regression tests • PhantomCSS • BackstopJS • Wraith

  34. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(“#signUpForm”, { name: “Alicia Sedlock”, email: “alicia@example.com” }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  35. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  36. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  37. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  38. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  39. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  40. casper.start(“/home”).then(function(){ // Initial state of form phantomcss .screenshot(“#signUpForm”, “sign up

    form”); // Hit the sign up button (should trigger error) casper.click(“button#signUp”); // Take a screenshot of the UI component phantomcss .screenshot(“#signUpForm”, “sign up form error”); // Fill in form by name attributes & submit casper.fill(‘#signUpForm', { name: ‘Alicia Sedlock’, email: ‘alicia@example.com’ }, true); // Take a second screenshot of success state phantomcss .screenshot(“#signUpForm”, “sign up form success”); });
  41. Visual regression tests </3 full page screenshots.

  42. Visual regression tests <3 components!

  43. casper.start(“/styleguide”).then(function(){ phantomcss .screenshot(“a.link”, “link example”); phantomcss .screenshot(“.btn-primary”, “primary button”); }).then(function()

    { this.mouse.move(“.btn-primary”'); phantomcss .screenshot(“.btn-primary”, “button hover”); });
  44. Visual regression tests <3 responsive design!

  45. phantomcss: { options: { mismatchTolerance: 0.05, screenshots: ‘tests/visual/baselines’, results: ‘tests/visual/results’,

    viewportSize: [1280, 800] }, src: [‘tests/visual/tests.js’] }
  46. phantomcss: { big_size: { options: { ... viewportSize: [1280, 800]

    } }, small_size: { options: { ... viewportSize: [480, 320] } } }
  47. Visual regression tests • The youngest of the test types

    • Test components, not full pages • Great for responsive testing
  48. But…

  49. But… I’ll still have bugs!

  50. But… I don’t have time!

  51. But… I already have code!

  52. Thank you, Up Front! Keep in touch. @alicibility