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

Javascript Testing with Jasmine

Roy Yu
October 11, 2014

Javascript Testing with Jasmine

This presentation is prepared for SVCC on Javascript Testing with Jasmine. It basically goes through basic Jasmine feature and provide tips for developers when they decide to start testing.

Roy Yu

October 11, 2014
Tweet

More Decks by Roy Yu

Other Decks in Technology

Transcript

  1. JAVASCRIPT
    TESTING WITH
    JASMINE
    ROY YU
    Silicon Valley Code Camp

    View Slide

  2. WHY UNIT TEST ?
    It acts as a “free” documentation for your code, they provide
    example to other developers of how your code works.

    View Slide

  3. WHY UNIT TEST ?
    It reduces bugs in new or existing features. It tells you right
    away if you someone’s code.

    View Slide

  4. WHY UNIT TEST ?
    It force you to understand the features and promote
    communication between technical and non-technical group.

    View Slide

  5. WHY UNIT TEST ?
    It reduces cost for software development or feature
    development.

    View Slide

  6. WHY UNIT TEST ?
    It improves your software / feature design. Thinking how to
    make a software testable ahead of time will decrease the
    complexity of your code.

    View Slide

  7. WHY UNIT TEST ?
    It defends against your coworkers when doing code reviews.

    View Slide

  8. WHY UNIT TEST ?
    Plus …

    View Slide

  9. WHY UNIT TEST ?
    Promote Better Sleeping pattern during night time.

    View Slide

  10. WHY UNIT TEST ?
    It makes you feel you are a super star
    … until someone in your team starts doing the same thing

    View Slide

  11. JAVASCRIPT
    TESTING WITH
    JASMINE
    ROY YU

    View Slide

  12. JASMINE – SETUP
    Go to >> jasmine.github.io

    View Slide

  13. JASMINE – SETUP

    View Slide

  14. JASMINE – SETUP

    View Slide

  15. JASMINE – SETUP

    View Slide

  16. JASMINE – SETUP

    View Slide

  17. JASMINE – VERY BASIC
    Jasmine is a behavior driven testing framework for testing
    javascript code.
    Note: Behavior driven development ( BDD ) emphasis on
    writing your spec against your feature behavior ( business
    requirement ) that are small and easy to read.
    •  Small: Your test just need to test one thing only.
    •  Easy to read: Your test should need only one sentence to
    describe what you are testing.

    View Slide

  18. JASMINE – VERY BASIC

    View Slide

  19. FIRST TEST
    describe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    var shark = new Shark();
    expect(shark.swim).not.toBeUndefined();
    });
    });
    // This is just an imaginary test

    View Slide

  20. SUITE
    describe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    var shark = new Shark();
    expect(shark.swim).not.toBeUndefined();
    });
    });
    Note: Suite typically defines a component of your
    application, it could be either a class or a function. The first
    argument take a “string”, and second take a function.

    View Slide

  21. SPEC
    describe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    var shark = new Shark();
    expect(shark.swim).not.toBeUndefined();
    });
    });
    Note: Inside suite are the ‘specs’. It is testing what a
    particular piece of your component or your code should do.
    Each suite can holds any number of specs.

    View Slide

  22. MATCHERS
    describe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    var shark = new Shark();
    expect(shark.swim).not.toBeUndefined();
    });
    });
    Note: Matchers are for validating your specs passing or not.
    It takes the argument inside expect() and check to see if it
    satisfies the criteria in the matcher.

    View Slide

  23. JASMINE - BEFOREEACH
    describe(‘Shark’, function() {
    var shark;
    beforeEach(function() { // this will run for each spec ( setUp )
    shark = new Shark();
    });
    it(“should know how to swim”, function() {
    expect(shark.swim).not.toBeUndefined();
    });
    it(“has a fin”, function() {
    // …
    });
    it(“has sharp teeth”, function() {
    // …
    });
    });

    View Slide

  24. AFTEREACH
    describe(‘Shark’, function() {
    var shark;
    beforeEach(function() {
    shark = new Shark();
    });
    afterEach(function() { // this will run after each spec ( tearDown )
    shark = null;
    });
    it(“has a fin”, function() {
    // …
    });
    it(“has sharp teeth”, function() {
    // …
    });
    });

    View Slide

  25. SKIP SPEC
    describe(‘Shark’, function() {
    xit(“should know how to swim”, function() {
    // this spec will be skipped by the test runner
    });
    it(“has a fin”, function() {
    // this spec will still be run
    });
    });

    View Slide

  26. SKIP SUITE
    xdescribe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    // …
    });
    it(“has a fin”, function() {
    // …
    });
    });
    The whole suite will be skipped by the test runner.

    View Slide

  27. NESTED SUITES
    describe(‘Shark’, function() {
    describe(“Sharks’ teeth”, function() {
    it(“blah …”, function() {
    // …
    });
    });
    describe(“Sharks’ skin”, function() {
    it(“another blah …”, function() {
    // …
    });
    });
    });

    View Slide

  28. MORE MATCHERS

    View Slide

  29. EQUALITY
    toEqual
    expect(true).toEqual(true);
    expect([‘a’, ‘b’]).toEqual([‘a’,’b’]);
    expect(6).toEqual(13);
    expect(false).toEqual(123);

    View Slide

  30. IDENTICAL
    toBe
    expect(a).toEqual(b);
    expect(a).toBe(a);
    expect(a).toBe(b);
    // toBe checks for if same object.
    var a = {test:”abc”};
    var b = {test:”abc”};

    View Slide

  31. YES / NO
    toBeTruthy / toBeFalsy
    expect(true).toBeTruthy();
    expect(12).toBeTruthy();
    expect(null).toBeFalsy();
    expect(false).toBeFalsy();
    expect(“”).toBeTruthy();
    expect(undefined).toBeTruthy();
    Expect(“hello”).toBeFalsy();

    View Slide

  32. CONTAIN ELEMENT
    toContain
    expect([1,3,5]).toContain(3);
    expect(“hello everyone”).toContain(“hello”);
    expect([
    { test: “abc”}, {test: “efg”}
    ]).toContain({test: “sos”});

    View Slide

  33. DEFINED OR NOT
    toBeDefined / toBeUndefined
    expect(myDefinedVariable).toBeDefined();
    expect(null).toBeDefined();
    expect(yourVariable).toBeDefined();
    expect(null).toBeUndefined();
    var myDefinedVariable;

    View Slide

  34. NULLNESS
    toBeNull
    expect(null).toBeNull();
    expect(false).toBeNull();
    expect(yourVariable).toBeNull();

    View Slide

  35. NAN
    toBeNaN
    expect(0/0).toBeNaN();
    expect(“hello everyone”).toBeNaN();
    expect(6).toBeNaN();

    View Slide

  36. COMPARISION
    toBeGreaterThan / toBeLessThan
    expect(8).toBeGreateThan(5);
    expect(“a”).toBeLessThan(“c”);
    expect(5).toBeGreaterThan(13);

    View Slide

  37. CLOSENESS
    toBeCloseTo
    expect(10.01).toBeCloseTo(10.08, 1);
    expect(10.012).toBeCloseTo(10.01, 2);
    expect(10.012).toBeCloseTo(12, 0);
    expect(10.01).toBeCloseTo(10.08, 2);
    expect(10.012).toBeCloseTo(10.01, 3);

    View Slide

  38. MATCHING
    toMatch
    expect(“hello world”).toMatch(/world/);
    expect(“hello world”).toMatch(/everyone/);

    View Slide

  39. THROW ERROR
    toThrow
    expect(_a_func_that_throw_error()).toThrow();
    // It takes a function and see if it throw error
    expect(function() {
    return “something”;
    }).toThrow();
    var _a_func_that_throw_error =
    function() {
    throw new Error();
    }

    View Slide

  40. NEGATION
    not
    expect(1).not.toBeGreaterThan(3);
    expect(true).not.toBeFalsy();
    expect(“hello world”).not.toBeDefined();

    View Slide

  41. CUSTOM MATCHER
    Jasmine.addMatchers({
    toBeGreaterThan: function() {
    return {
    compare: function(actual, expected) {
    return {
    pass: actual > expected
    }
    }
    }
    }
    });

    View Slide

  42. RECAP
    Let’s do some exercise
    Read the following sentences …
    I learn today Jasmine is about …
    describe, it, using expect and matchers
    describe, it, using expect and matchers
    describe, it, using expect and matchers

    View Slide

  43. RECAP
    Well, not totally true in previous slide, but true by so far.
    describe(‘Shark’, function() {
    it(“should know how to swim”, function() {
    var shark = new Shark();
    expect(shark.swim).not.toBeUndefined();
    });
    });

    View Slide

  44. TESTING SPIES

    View Slide

  45. BASE CLASS
    var Shark = function() {}
    Shark.prototype.greet = function(thing) {
    this.eat(thing);
    }
    Shark.prototype.eat = function(thing) {
    this.say(thing);
    }
    Shark.prototype.say = function(thing) {
    return thing + “ is so yummy”;
    }

    View Slide

  46. POPUP QUIZ
    In plain English, can you
    describe a “Shark” by this
    definition ?
    var Shark = function() {}
    Shark.prototype.greet = function(thing) {
    this.eat(thing);
    }
    Shark.prototype.eat = function(thing) {
    this.say(thing);
    }
    Shark.prototype.say = function(thing) {
    return thing + “ is so yummy”;
    }

    View Slide

  47. SPY BASIC
    describe(“Shark”, function() {
    it(‘eats whatever it greets’,
    function() {
    var shark = new Shark();
    spyOn(shark, ‘greet’);
    shark.greet(‘duck’);
    expect(shark.eat)
    .toHaveBeenCalled();
    }
    });
    var Shark = function() {}
    Shark.prototype.greet = function(thing) {
    this.eat(thing);
    }
    Shark.prototype.eat = function(thing) {
    this.say(thing);
    }
    Shark.prototype.say = function(thing) {
    return thing + “ is so yummy”;
    }

    View Slide

  48. SPY BASIC
    With spy, you can do the following as well
    •  expect(shark.eat).toHaveBeenCalledWith(‘duck’)
    •  expect(shark.eat.callCount).toBe(1)
    You can also force a spy function to return stuff you defined
    •  spyOn(shark, ‘say’).andReturn(‘Meh …’);
    // Note that the first argument for spyOn is the object, second
    argument is the method in string format

    View Slide

  49. SPY BASIC
    Some catcha …
    By default, spy will replace the behavior of your spied
    function, some people illustrate it as “eating the function”. In
    order to use spy as well as delegate the spied function back
    to the actual implementation, you can do the following
    spyOn(shark, ‘eat’).andCallThrough();

    View Slide

  50. SPY BASIC
    describe(“Shark”, function() {
    it(‘eats whatever it greets’,
    function() {
    var shark = new Shark();
    spyOn(shark, ‘say’);
    spyOn(shark, ‘eat’)
    .andCallThrough();
    shark.greet(‘duck’);
    expect(shark.eat)
    .toHaveBeenCalled();
    expect(shark.say)
    .toHaveBeenCalled();
    });
    var Shark = function() {}
    Shark.prototype.greet = function(thing) {
    this.eat(thing);
    }
    Shark.prototype.eat = function(thing) {
    this.say(thing);
    }
    Shark.prototype.say = function(thing) {
    return thing + “ is so yummy”;
    }

    View Slide

  51. RECAP
    In short, spy records its interaction with other objects in your
    test.
    For example, you can retrieve run time statistic on the spied
    function.
    •  How many times the function was called
    •  What was the value return to the caller
    •  How many parameters the function was called with

    View Slide

  52. RECAP
    There are more concepts to Jasmine when coming to test
    doubles. In javascript testing world, there are spies, stubs
    and mocks. They could look very similar, but their existence
    are for different use cases. We will introduce other library
    that make it easy to understand in reference section.
    But for now, learning what spy means in Jasmine will give
    you what you need to write test.

    View Slide

  53. ASYNC TESTING

    View Slide

  54. ASYNC TESTING
    In javascript, there are times you may need to test ‘events’, ‘ajax
    calls’ type of asynchronous actions.
    For example :
    // Assume after shark finishing its meal, it will send a signal ( event ) out ?
    describe(‘Shark’, function() {
    it(‘should say something after finishing its meal’,
    function() {
    }
    });

    View Slide

  55. ASYNC TESTING
    var Shark = function() {
    this.on(‘finish_eating’, function(evt, payload) {
    this.say(payload.item);
    }, this);
    }
    Shark.prototype.eat = function(thing) {
    this.trigger(‘finish_eating’, {item:thing});
    }

    View Slide

  56. ASYNC TESTING
    In Jasmine, there are a few functions called runs, waits and
    waitsFor to support running specs for testing async operation.
    The sequence usually looks like :-
    // code that starts the async process
    runs
    // wait for something happen or timeout
    waitsFor
    // after waitsFor done its job, the next runs block will execute
    runs

    View Slide

  57. ASYNC TESTING
    describe(“Shark”, function() {
    it(‘say something after finish eating’, function() {
    var shark = new Shark();
    spyOn(shark, ‘say’);
    runs(function() { // start the async support
    shark.eat(‘duck’);
    });
    waitsFor(function() { // wait for something happen and return true, or it will time out
    return shark.say.callCount == 1;
    }, “Shark should say something”, 500);
    runs(function() { // still inside the async support
    expects(shark.say.callCount).toBeGreaterThan(0);
    });
    }
    });

    View Slide

  58. You just finish the basic features
    of Jasmine ~ Hopefully you still
    remember what you just
    learned :-)

    View Slide

  59. Now go ahead and sleep better
    from now on ~

    View Slide

  60. But Wait !!

    View Slide

  61. Jasmine is just the
    Beginning …

    View Slide

  62. There are
    more in JS testing …

    View Slide

  63. It’s time to use your
    notepad

    View Slide

  64. http://karma-runner.github.io/
    http://phantomjs.org/
    http://sinonjs.org/
    http://jasmine.github.io/
    http://gotwarlost.github.io/istanbul/
    http://docs.busterjs.org/en/latest/
    http://requirejs.org/
    https://github.com/velesin/jasmine-jquery
    http://gruntjs.com/

    View Slide

  65. Some for you ~

    View Slide

  66. TDD
    TDD and BDD go together hands in hands, not really
    competitors.
    The emphasis of TDD is your test is ahead of your
    implementation and it follows the Red Green Refactor
    pattern.
    Red à you write test that fail
    Green à you write basic implementation to pass the test
    Refactor à write you real implementation based on
    requirement

    View Slide

  67. TDD
    But let’s face it
    It is just a theory
    And you don’t aim for 100% test coverage!!

    View Slide

  68. TDD
    But it’s still good
    to practice it
    At least there are some coverage to start with!!

    View Slide

  69. BDD
    BDD could be considered as
    an extension to TDD
    The emphasis is more on business case and the
    purpose of your work

    View Slide

  70. BDD
    It combines two different
    activities into one
    •  The creation of Unit Tests and implementations
    •  The creation of Functional Tests and features

    View Slide

  71. THINK TEST FIRST
    Try to ask yourself next time when you start writing your
    code à “How can I test it?, Is this function testable?” It will
    help yourself to minimize the complexity of your code.
    If your existing function / class is doing too many things at
    the same time, try to write some specs and it will help your
    refactoring.
    Remember, your function should just need to do one thing
    and do it good.

    View Slide

  72. COMMUNICATION
    Unit test is not just about Green light or Red light, it is also
    about communication. Make sure you are writing the right
    specs, and making the right assumption.
    Invest more time with your manager / product / lead to truly
    understand what you are implementing.
    Just as what mentioned in the first few slides, writing/
    planning test promotes communication between technical
    and non technical group.

    View Slide

  73. CONVERT TEST SPECS
    When you receive your requirement from your manager /
    product, it’s already come with “expected behavior” inside
    the “user story”.

    View Slide

  74. REFACTOR LEGACY CODE
    If code are not developed with testable in mind, it should
    have a lot of dependencies, for example, talk to database,
    sending emails. One of the task of refactoring code be to
    testable is first breaking the dependencies. After that, you
    want to check if any of your methods are doing too much,
    and there is clear role of that function. You can call this step
    reducing complexity.

    View Slide

  75. WHAT TO TEST
    You should test thing that is going to last. For example, you
    won’t test the a particular flow that is under A/B testing,
    since it won’t last long.
    You should test your critical path that deliver business
    values. If you have bunch of getter and setter, some people
    will actually exclude that, because it doesn’t deliver real
    values to the business.
    The main guide line is to think and apply whenever it makes
    sense, there is no point to aim for 100% coverage.

    View Slide

  76. NO DEPENDENCIES
    As a frontend dev, your unit test is
    probably not done correctly if …
    1.  You need to do a real ajax call
    2.  You instantiate a class in your test that is not what you
    are testing
    3.  Other external data source
    For all those dependencies, you need to stub it out or mock it
    out. Your unit test should be isolated on its own.

    View Slide

  77. REMEMBER
    Unit testing is about testing as a
    (isolated) unit. So for all other
    dependencies, you will need to create
    stub, mock, fakeServer or fixture!!

    View Slide

  78. IT’S DEMO TIME

    View Slide

  79. Questions ?!

    View Slide

  80. THANK YOU
    https://www.linkedin.com/in/iroy2000
    [email protected]

    View Slide