jasmine - why js tests don't smell fishy

41
1 JASMINE Why JavaScript tests don’t smell fishy?

Upload: igor-napierala

Post on 06-May-2015

2.958 views

Category:

Technology


1 download

DESCRIPTION

Presentation about Jasmine - BDD

TRANSCRIPT

Page 1: Jasmine - why JS tests don't smell fishy

1

JASMINEWhy JavaScript tests don’t smell fishy?

Page 2: Jasmine - why JS tests don't smell fishy

WHAT I WILL TALK ABOUT:

2

• What is Unit Testing

• Solution: Jasmine - whole syntax with examples

• WebStorm goodies

• Benefits of Units Tests

• Why people don’t test JavaScript?

• Test Driven Development, Behavior Driven Development

Page 3: Jasmine - why JS tests don't smell fishy

WHY PEOPLE DON’T TEST JAVASCRIPT ?

• project not big enough

• project too complex

• asynchronous XHR requests

• DOM manipulation

• too many browsers, platforms, devices

3

Page 4: Jasmine - why JS tests don't smell fishy

UNIT TESTING is a program (test case or test specification) that isolates and tests small and specific functional unit of code.

Test one small functionality per suite. Not too many things at one time.Remember! It impossible to write test for every case - try to cover every reasonable case, remember about corner cases

4

GOOD PRACTICES

Page 5: Jasmine - why JS tests don't smell fishy

BENEFITS OF USING JAVASCRIPT TESTS

5

• QA phase is cheaper - you will uncover bugs earlier

• Creates great documentation

• As a developer you will write better code

• Shows that JS should work as was designed

• Quick and easy to run - try to do it with manual testing

• Runs the same every time

Page 6: Jasmine - why JS tests don't smell fishy

TDD & BDD

Behavior Driven Development: agile software development technique testing from business value perspective, why some code is necessary and what its goal is

6

Test Driven Development: write tests against specification, watch your test fail, write some code, test, refactor, test-fix-implement

Page 7: Jasmine - why JS tests don't smell fishy

TDD vs BDDExample: 10 sorting methods

TDD: one test per one method - focused on „how” each method works

BDD:one test per all methods - focused on the goal give an array, sort, result

7

Page 8: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - EQUALITYexpect(true).toEqual(true);expect({}).toEqual({});

8toEqual

Page 9: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - IDENTITY

var spot = { species: "Border Collie" };

var cosmo = { species: "Border Collie" };

// success; equivalent

expect(spot).toEqual(cosmo);

// failure; not the same object

expect(spot).toBe(cosmo);

// success; the same value

expect(2).toBe(2);

toBe

checks if two things are the same value and type, using ===

Primitive Types vs Reference Typesprimitive will be give you true, reference will give you false

9

Page 10: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - BOOLEAN

expect(true).toBeTruthy();expect(12).toBeTruthy();expect({}).toBeTruthy();

expect(false).toBeFalsy();expect(null).toBeFalsy();expect("").toBeFalsy();//false, 0, “”, undefinded, null, NaN

toBeTruthy toBeFalsy10

Page 11: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - NEGATIONexpect(this).not.toEqual(that);expect({}).not.toBe([]);

not11

Page 12: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - CONTAINSexpect([1, 2, 3, 4]).toContain(3);expect(["Jasmine", "Qunit", "Mocha", "Casper"]).toContain("Jasmine");expect("Rychu Peja to pener").toContain("pener");

var dog = { name: "Reksio" };expect([{ name: "Dżeki" },{ name: "Max" },{ name: "Reksio" }]).toContain(dog);

toContain12

Page 13: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - DEFINED OR UNDEFINED

var somethingUndefined;

expect("Hello!").toBeDefined(); // successexpect(null).toBeDefined(); // successexpect(somethingUndefined).toBeDefined(); // failure

var somethingElseUndefined;expect(somethingElseUndefined).toBeUndefined(); // successexpect(2013).toBeUndefined(); // failureexpect(null).toBeUndefined(); // failure

toBeDefined toBeUndefined13

Page 14: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - NULLNESS

expect(null).toBeNull(); // successexpect(false).toBeNull(); // failureexpect(somethingUndefined).toBeNull(); // failure

//null is where the thing is known to exist, //but it's not known what the value is.

toBeNull14

Page 15: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - IS NaN

expect(5).not.toBeNaN(); // success

expect(0 / 0).toBeNaN(); // successexpect(parseInt("hello")).toBeNaN(); // success

/* This is different from JavaScript’s built-in isNaN function. The built-in isNaN will return true for many nonnumber types, such as nonnumeric strings, objects, and arrays. Jasmine’s will be positive only if it’s the NaN value.*/

toBeNaN15

Page 16: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - COMPARATORS

expect(8).toBeGreaterThan(5);

expect(5).toBeLessThan(12);expect("a").toBeLessThan("z"); // it works for strings

toBeGreaterThan toBeLessThan16

Page 17: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - NEARNESS

expect(12.34).toBeCloseTo(12.3, 1); // success

toBeCloseTo17

Page 18: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - REGULAR EXPRESSIONS

expect("some words").toMatch(/some/); // success

toMatch18

Page 19: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - ERROR THROWING

var errorThrower = function () {

throw new Error();

}

expect(errorThrower).toThrow(); // success

toThrow19

Page 20: Jasmine - why JS tests don't smell fishy

JASMINE - BEFORE AND AFTER TESTS

var player, wallet; // remember about scope

beforeEach( function () { player = new Player;});

afterEach( function () { wallet.empty(); // empty after each test});

beforeEach afterEach20

Page 21: Jasmine - why JS tests don't smell fishy

JASMINE MATCHERS - CUSTOM MATCHERSbeforeEach( function () { this.addMatchers({ toBeLarge: function () { this.message = function () { return "Expected " + this.actual + " to be large"; }; return this.actual > 100; } });});

expect(5).toBeLarge(); // failureexpect(101).toBeLarge(); // success

custom matchers21

Page 22: Jasmine - why JS tests don't smell fishy

JASMINE - NESTED SUITSdescribe("Expected ", function () {

describe("something ", function () {

it("should do something", function () { expect(2).toBe(2); });

});

});

22describe describe

Page 23: Jasmine - why JS tests don't smell fishy

JASMINE - SKIP THE TESTdescribe("Expected ", function () {

xdescribe("something ", function () {

xit("should do something", function () { expect(2).toBe(2); });

return;

it("should do something else", function () { expect(3).toBe(3); });

});

});

23xit xdescribe return

Page 24: Jasmine - why JS tests don't smell fishy

JASMINE - SPIESvar Dictionary = function() {}, Person = function() {};

Dictionary.prototype.hello = function () { return "hello";};

Dictionary.prototype.world = function () { return "world";};

Person.prototype.sayHelloWorld = function(dict) { return dict.hello() + " " + dict.world();};

var dictionary = new Dictionary, person = new Person;

person.sayHelloWorld(dictionary); // returns "hello world"

describe("Person", function() {

it('uses the dict to say "hello world"', function() { var dictionary = new Dictionary, person = new Person;

// replace each function with a spy spyOn(dictionary, "hello"); spyOn(dictionary, "world"); person.sayHelloWorld(dictionary);

// not possible without first spies expect(dictionary.hello).toHaveBeenCalled(); expect(dictionary.world).toHaveBeenCalled(); });

});

toHaveBeenCalled

Often you test more than variable checks. Spy can pretend that he is a function or an object.

24

Page 25: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can call through the function

describe("Person", function() { it('uses the dictionary to say "hello world"', function () { var dictionary = new Dictionary, person = new Person; spyOn(person, "sayHelloWorld"); // replace hello world function with a spy person.sayHelloWorld(dictionary);

expect(person.sayHelloWorld).toHaveBeenCalledWith(dictionary);

});

});

toHaveBeenCalledWith25

Page 26: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can return specific value

it("spy can return specific value", function () { var dictionary = new Dictionary, person = new Person, result;

spyOn(dictionary, "hello").andReturn("Witaj"); result = person.sayHelloWorld(dictionary); expect(result).toEqual("Witaj world");});

andReturn26

Page 27: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can count its calls

callCount

it("can count calls of spy", function () { var dictionary = new Dictionary, spy;

spy = spyOn(dictionary, "hello"); dictionary.hello();

expect(spy.callCount).toEqual(1);});

27

Page 28: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can get recent arguments

mostRecentCall.args

it("can give you last arguments", function () { var dictionary = new Dictionary, person = new Person, spy;

spy = spyOn(person, "sayHelloWorld"); person.sayHelloWorld("No siemano"); // remember arguments will be in array expect(spy.mostRecentCall.args).toEqual(["No siemano"]);});

28

Page 29: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can get arguments of specific call

argsForCall[index]

it("can give you last arguments", function () { var dictionary = new Dictionary, person = new Person, spy;

spy = spyOn(person, "sayHelloWorld"); person.sayHelloWorld("No siemano"); person.sayHelloWorld("Hejka"); // remember arguments will be in array and argForCall is also array expect(spy.argsForCall[1]).toEqual(["Hejka"]);});

29

Page 30: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can call fake functions

it("can call a fake function", function() { var fakeHello = function() { console.log("I’m a fake function"); return "hello"; };

var dictionary = new Dictionary();

spyOn(dictionary, "hello").andCallFake(fakeHello);

dictionary.hello(); // does an log});

andCallFake30

Page 31: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can be created on its own

it("can have a spy function", function() { var person = new Person();

person.getName = jasmine.createSpy("Name spy"); person.getName();

expect(person.getName).toHaveBeenCalled();});

jasmine.createSpy31

Page 32: Jasmine - why JS tests don't smell fishy

32

JASMINE - SPIES can chain actions

person.getSecretAgentName = jasmine.createSpy("Name spy").andReturn("James Bond");

chaning

Page 33: Jasmine - why JS tests don't smell fishy

JASMINE - SPIES can be an object

it("spy can be an object", function() {

var player = jasmine.createSpyObj("player", ["hustle", "rap"]); player.hustle(); // magic to test});

jasmine.createSpyObj33

Page 34: Jasmine - why JS tests don't smell fishy

JASMINE - asynchronous - RUNS

it("runnin’", function() { runs(function() { it("should do something", function () { expect(2).toBe(2); }); });});

runs34

Page 35: Jasmine - why JS tests don't smell fishy

JASMINE - asynchronous - WAITS it('should be a test', function () { runs(function () { this.foo = 0; var that = this; setTimeout(function () { that.foo++; }, 250); });

runs(function () { expect(this.foo).toEqual(0); });

waits(1000);

runs(function () { expect(this.foo).toEqual(1); }); });

waits35

Page 36: Jasmine - why JS tests don't smell fishy

36

JASMINE - asynchronous - WAITS FOR

describe('asynchronous wait for', function() { it('should wait for something', function () { var spreadsheet = new Spreadsheet();

waitsFor(function() { return true; // here you can call your function which should be true }, "Something went wrong", 3000);

runs(function () { expect(2).toEqual(2); }); }); });

waitsFor(function, opt message, opt timeout)

Page 37: Jasmine - why JS tests don't smell fishy

JASMINE - jQuery matchers

toBe(jQuerySelector)toBeChecked()toBeEmpty()toBeHidden()toHaveCss(css)toBeSelected()toBeVisible()toContain(jQuerySelector)toExist()toHaveAttr(attributeName, attributeValue)toHaveProp(propertyName, propertyValue)toHaveBeenTriggeredOn(selector)toHaveBeenTriggered()toHaveBeenTriggeredOnAndWith(selector, extraParameters)

toHaveBeenPreventedOn(selector)toHaveBeenPrevented()toHaveClass(className)toHaveData(key, value)toHaveHtml(string)toContainHtml(string)toContainText(string)toHaveId(id)toHaveText(string)toHaveValue(value)toHaveLength(value)toBeDisabled()toBeFocused()toHandle(eventName)toHandleWith(eventName, eventHandler)

37

Page 38: Jasmine - why JS tests don't smell fishy

JASMINE - HTML & JSON fixtures

//myfixture.html<div id="my-fixture">some complex content here</div>

//some fixture addingloadFixtures('myfixture.html');$('#my-fixture').myTestedPlugin();expect($('#my-fixture')).toBe('div');

38

Page 39: Jasmine - why JS tests don't smell fishy

JASMINE for AJAX

39

Sinon

Jasmine AJAX

Page 40: Jasmine - why JS tests don't smell fishy

Github Present for WebStorm users

40

https://github.com/thisisui/jetbrains-jasmine-test-startup

Page 41: Jasmine - why JS tests don't smell fishy

41

THANKS FOR YOUR TIME

QUESTIONS?