Slide 1

Slide 1 text

photo by quaziefoto Auomaed Testing in JavaScript ৷ԈᎲ @WebDev Party #1

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

You can get this slide on www.eddie.com.tw/slides

Slide 4

Slide 4 text

a.k.a Eddie or Aquarianboy Live and work in Taipei, Taiwan. Serving in my own little tiny company. Flash / AS3 / Ruby / Rails / Python programming for living. A little bit Objective-C for personal inerests. Technical Education and Consulant. PTT Flash BM (since 2007/4). Adobe Certificaed Flash Developer (Since 2006/7). Linux Professional Institue Certification (Since 2005/3). ৷ԈᎲ photo by Eddie

Slide 5

Slide 5 text

anyone do est?

Slide 6

Slide 6 text

people don’t est..

Slide 7

Slide 7 text

In your company, who does these ests?

Slide 8

Slide 8 text

does they really know how o do est?

Slide 9

Slide 9 text

not just launch a browser, and click.. click.. click..

Slide 10

Slide 10 text

how do you developers est?

Slide 11

Slide 11 text

Sep1: Wrie some code!

Slide 12

Slide 12 text

Sep 2: Ctrl + S = Save!

Slide 13

Slide 13 text

Sep 3: Alt + Tab

Slide 14

Slide 14 text

Sep 4: F5

Slide 15

Slide 15 text

Sep 5: and.. repeat the Sep 1

Slide 16

Slide 16 text

Browser plugins or console!

Slide 17

Slide 17 text

LiveReload!

Slide 18

Slide 18 text

Selenium

Slide 19

Slide 19 text

What’s Auomaed Testing?

Slide 20

Slide 20 text

Auomaed esting means that ests are run every single time a file is saved.

Slide 21

Slide 21 text

Test Level 1: by eyes, hands, and your instinct.

Slide 22

Slide 22 text

Test Level 2: ALERT, or Console.log()

Slide 23

Slide 23 text

Test Level 3: Test Runner

Slide 24

Slide 24 text

Test Level 4: Test Framework

Slide 25

Slide 25 text

Test Level 5: Test Framework + Auomaed Testing Tools

Slide 26

Slide 26 text

Why est?

Slide 27

Slide 27 text

Cross-browser issues !! some browsers just don’t die.

Slide 28

Slide 28 text

less bugs == you have more time developing new features.

Slide 29

Slide 29 text

Why est JavaScript? cause JS is popular now, and it’s getting more and more complicaed.

Slide 30

Slide 30 text

Why est JavaScript auomatically? cause we’re Lazzzzzzzzzzzy!!

Slide 31

Slide 31 text

What’s Unit Test?

Slide 32

Slide 32 text

Unit est is a piece of code that ests a piece of production code. so, unit est might be also Buggy!

Slide 33

Slide 33 text

a good unit est should be short and focus on a single behavior of a function/method.

Slide 34

Slide 34 text

the heart of a unit est is the assertion.

Slide 35

Slide 35 text

How can code be esable?

Slide 36

Slide 36 text

Not every code can be easily esed.

Slide 37

Slide 37 text

TDD

Slide 38

Slide 38 text

Twiter-Driven Development

Slide 39

Slide 39 text

Test-Driven Development

Slide 40

Slide 40 text

Test-first development is hard — it’s hard because it forward-shifts your confusion.

Slide 41

Slide 41 text

Test != Debug

Slide 42

Slide 42 text

xUnit

Slide 43

Slide 43 text

Test should be fast, and easy o run repeaedly.

Slide 44

Slide 44 text

est case, est suie, est runner

Slide 45

Slide 45 text

Spy, Stub, and Mock FAKE!! something not REALLY!

Slide 46

Slide 46 text

spy “Spies are functions that keep track of how and ofen they were called, and what values were returned.” This is useful in asynchronous and event- driven applications.

Slide 47

Slide 47 text

Sinon.js it "check if 'strip_tag' method was triggered", -> spy = sinon.spy() namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' namecard.bind 'strip_tag', spy namecard.trigger 'strip_tag' # Expect the spy was called at least once expect(spy.called).toBeTruthy()

Slide 48

Slide 48 text

Sinon.js it "check if 'strip_tag' method was triggered", -> spy = sinon.spy() namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' namecard.bind 'strip_tag', spy namecard.trigger 'strip_tag' # Expect the spy was called at least once expect(spy.called).toBeTruthy()

Slide 49

Slide 49 text

Sinon.js it "check if 'strip_tag' method was triggered", -> spy = sinon.spy() namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' namecard.bind 'strip_tag', spy namecard.trigger 'strip_tag' # Expect the spy was called at least once expect(spy.called).toBeTruthy()

Slide 50

Slide 50 text

Sinon.js it "check if 'strip_tag' method was triggered", -> spy = sinon.spy() namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' namecard.bind 'strip_tag', spy namecard.trigger 'strip_tag' # Expect the spy was called at least once expect(spy.called).toBeTruthy()

Slide 51

Slide 51 text

Sinon.js it "check if 'strip_tag' method was triggered", -> spy = sinon.spy() namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' namecard.bind 'strip_tag', spy namecard.trigger 'strip_tag' # Expect the spy was called at least once expect(spy.called).toBeTruthy()

Slide 52

Slide 52 text

Sinon.js it "check if ajax method was triggered while saving", -> namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' spy = sinon.spy jQuery, 'ajax' namecard.save(); # check Spy was called expect(spy).toHaveBeenCalled() # Check url property of first argument expect(spy.getCall(0).args[0].url).toEqual "/namecard/1"

Slide 53

Slide 53 text

Sinon.js it "check if ajax method was triggered while saving", -> namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' spy = sinon.spy jQuery, 'ajax' namecard.save(); # check Spy was called expect(spy).toHaveBeenCalled() # Check url property of first argument expect(spy.getCall(0).args[0].url).toEqual "/namecard/1"

Slide 54

Slide 54 text

Sinon.js it "check if ajax method was triggered while saving", -> namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' spy = sinon.spy jQuery, 'ajax' namecard.save(); # check Spy was called expect(spy).toHaveBeenCalled() # Check url property of first argument expect(spy.getCall(0).args[0].url).toEqual "/namecard/1"

Slide 55

Slide 55 text

Sinon.js it "check if ajax method was triggered while saving", -> namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' spy = sinon.spy jQuery, 'ajax' namecard.save(); # check Spy was called expect(spy).toHaveBeenCalled() # Check url property of first argument expect(spy.getCall(0).args[0].url).toEqual "/namecard/1"

Slide 56

Slide 56 text

Sinon.js it "check if ajax method was triggered while saving", -> namecard = new app.NameCard name: 'eddie' tel: '0928617687' address: 'Taipei, Taiwan' spy = sinon.spy jQuery, 'ajax' namecard.save(); # check Spy was called expect(spy).toHaveBeenCalled() # Check url property of first argument expect(spy.getCall(0).args[0].url).toEqual "/namecard/1"

Slide 57

Slide 57 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 58

Slide 58 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 59

Slide 59 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 60

Slide 60 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 61

Slide 61 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 62

Slide 62 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 63

Slide 63 text

Sinon.js it "check if ajax method was triggered while saving", -> server = sinon.fakeServer.create() spy = sinon.spy() server.respondWith("GET", "/namecard/1", [200, {"Content-Type": "application/json"}, '{"id":1,"name":"eddie", "tel":"0928617687""}']); namecard = new NameCard({id: 1}) namecard.bind 'change', spy namecard.fetch() server.respond() # Expect that the spy was called with the new model expect(spy.called).toBeTruthy() expect(spy.getCall(0).args[0].attributes).toEqual id: 1 name: "eddie" name: "0928617687" server.restore()

Slide 64

Slide 64 text

Demo

Slide 65

Slide 65 text

Demo Browser Console

Slide 66

Slide 66 text

Demo QUnit

Slide 67

Slide 67 text

Demo Zombie.js with Node.js

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Demo Jasmine in Ruby on Rails 3.1

Slide 71

Slide 71 text

Demo Jasmine + Guard + Phanom.js in Ruby on Rails 3.1

Slide 72

Slide 72 text

Conclusions

Slide 73

Slide 73 text

Writing ests is an investment.

Slide 74

Slide 74 text

not all ests are good!

Slide 75

Slide 75 text

If you wrie bad ests, you might find that you gain none of the benefits, and insead are stuck with a bunch of ests that are time-consuming and hard o mainain.

Slide 76

Slide 76 text

In est-driven development ests are writen as specification before writing production.

Slide 77

Slide 77 text

Proper est-driven development ensures that a sysem will never conain code that is not being execued.

Slide 78

Slide 78 text

TDD will not auomatically make great designs.

Slide 79

Slide 79 text

TDD requires us o think about the results before providing the solution.

Slide 80

Slide 80 text

Don’t fear hard-coding!

Slide 81

Slide 81 text

Unit Test = You Need Test!

Slide 82

Slide 82 text

Just give it a try!

Slide 83

Slide 83 text

Any Question? photo by jamuraa

Slide 84

Slide 84 text

৷ԈᎲ Conacts photo by Eddie Websie Blog Plurk Facebook Google Plus Twiter Email Mobile http://www.eddie.com.tw http://blog.eddie.com.tw http://www.plurk.com/aquarianboy http://www.facebook.com/eddiekao http://www.eddie.com.tw/+ https://twiter.com/#!/eddiekao [email protected] +886-928-617-687