Slide 1

Slide 1 text

JAVASCRIPT TESTING WITH JASMINE ROY YU Silicon Valley Code Camp

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

WHY UNIT TEST ? Plus …

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

JAVASCRIPT TESTING WITH JASMINE ROY YU

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

JASMINE – SETUP

Slide 14

Slide 14 text

JASMINE – SETUP

Slide 15

Slide 15 text

JASMINE – SETUP

Slide 16

Slide 16 text

JASMINE – SETUP

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

JASMINE – VERY BASIC

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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.

Slide 21

Slide 21 text

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.

Slide 22

Slide 22 text

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.

Slide 23

Slide 23 text

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() { // … }); });

Slide 24

Slide 24 text

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() { // … }); });

Slide 25

Slide 25 text

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 }); });

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

MORE MATCHERS

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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”};

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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);

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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(); }

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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(); }); });

Slide 44

Slide 44 text

TESTING SPIES

Slide 45

Slide 45 text

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”; }

Slide 46

Slide 46 text

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”; }

Slide 47

Slide 47 text

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”; }

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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();

Slide 50

Slide 50 text

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”; }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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.

Slide 53

Slide 53 text

ASYNC TESTING

Slide 54

Slide 54 text

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() { } });

Slide 55

Slide 55 text

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}); }

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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); }); } });

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Now go ahead and sleep better from now on ~

Slide 60

Slide 60 text

But Wait !!

Slide 61

Slide 61 text

Jasmine is just the Beginning …

Slide 62

Slide 62 text

There are more in JS testing …

Slide 63

Slide 63 text

It’s time to use your notepad

Slide 64

Slide 64 text

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/

Slide 65

Slide 65 text

Some for you ~

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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.

Slide 72

Slide 72 text

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.

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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.

Slide 75

Slide 75 text

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.

Slide 76

Slide 76 text

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.

Slide 77

Slide 77 text

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!!

Slide 78

Slide 78 text

IT’S DEMO TIME

Slide 79

Slide 79 text

Questions ?!

Slide 80

Slide 80 text

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