javascript unit testing with jasmine
DESCRIPTION
Presentation in javascript.lv meetupTRANSCRIPT
JavaScriptunit testing
with
Raimonds Simanovskis github.com/rsim
@rsim
The easiest Business Intelligence tool on the Web
Using threadsin Ruby applications
Using threadsin JavaScript applications
You can’t :)
JavaScriptunit testing
with
RSpec-like testing for JavaScript
Syntaxdescribe("A suite", function() { it("contains spec with an expectation", function() { expect(true).toBe(true); });});
describe("A suite is just a function", function() { var a;
it("and so is a spec", function() { a = true;
expect(a).toBe(true); });});
Matchers
describe("The 'toBe' matcher compares with ===", function() {
it("and has a positive case ", function() { expect(true).toBe(true); });
it("and can have a negative case", function() { expect(false).not.toBe(true); });
});
Included matchers
toBetoEqualtoMatchtoBeDefinedtoBeUndefinedtoBeNulltoBeTruthytoBeFalsy
toContaintoBeLessThantoBeGreaterThantoBeCloseTotoThrow
Setup & teardowndescribe("A spec (with setup and tear-down)", function() { var foo;
beforeEach(function() { foo = 0; foo += 1; });
afterEach(function() { foo = 0; });
it("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); });
it("can have more than one expectation", function() { expect(foo).toEqual(1); expect(true).toEqual(true); });});
Nestingdescribe("A spec", function() { var foo; beforeEach(function() { foo = 0; foo += 1; }); afterEach(function() { foo = 0; }); it("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); }); it("can have more than one expectation", function() { expect(foo).toEqual(1); expect(true).toEqual(true); }); describe("nested inside a second describe", function() { var bar; beforeEach(function() { bar = 1; }); it("can reference both scopes as needed ", function() { expect(foo).toEqual(bar); }); });});
Disabling
xdescribe("A spec", function() { var foo;
beforeEach(function() { foo = 0; foo += 1; });
xit("is just a function, so it can contain any code", function() { expect(foo).toEqual(1); });});
Spiesdescribe("A spy", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; spyOn(foo, 'setBar'); foo.setBar(123); foo.setBar(456, 'another param'); }); it("tracks that the spy was called", function() { expect(foo.setBar).toHaveBeenCalled(); }); it("tracks its number of calls", function() { expect(foo.setBar.calls.length).toEqual(2); }); it("tracks all the arguments of its calls", function() { expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456, 'another param'); }); it("allows access to the most recent call", function() { expect(foo.setBar.mostRecentCall.args[0]).toEqual(456); }); it("allows access to other calls", function() { expect(foo.setBar.calls[0].args[0]).toEqual(123); }); it("stops all execution on a function", function() { expect(bar).toBeNull(); });});
andReturndescribe("A spy, when faking a return value", function() { var foo, bar, fetchedBar; beforeEach(function() { foo = { setBar: function(value) { bar = value; }, getBar: function() { return bar; } }; spyOn(foo, 'getBar').andReturn(745); foo.setBar(123); fetchedBar = foo.getBar(); }); it("tracks that the spy was called", function() { expect(foo.getBar).toHaveBeenCalled(); }); it("should not effect other functions", function() { expect(bar).toEqual(123); }); it("when called returns the requested value", function() { expect(fetchedBar).toEqual(745); });});
Async testsdescribe("Asynchronous specs", function() { var value, flag; it("should support async execution of test preparation and exepectations", function() { runs(function() { flag = false; value = 0; setTimeout(function() { flag = true; }, 500); }); waitsFor(function() { value++; return flag; }, "The Value should be incremented", 750); runs(function() { expect(value).toBeGreaterThan(0); }); });});
Jasmine tests runner
Demo
Works nicewith CoffeeScriptand Backbone.js
Testing Backbone.js model
describe "Todo", -> todo = null ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param]
beforeEach -> todo = new TodoApp.Todo todos = new TodoApp.TodoList [todo]
it "should initialize with empty content", -> expect(todo.get "content").toEqual "empty todo..."
it "should initialize as not done", -> expect(todo.get "done").toBeFalsy()
it "should save after toggle", -> spyOn jQuery, "ajax" todo.toggle() expect(ajaxCall "url").toEqual "/todos" expect(todo.get "done").toBeTruthy()
and collection
describe "TodoList", -> attributes = [ content: "First" done: true , content: "Second" ] todos = null
beforeEach -> todos = new TodoApp.TodoList attributes it "should return done todos", -> expect(_.invoke todos.done(), "toJSON").toEqual [attributes[0]]
it "should return remaining todos", -> expect(_.invoke todos.remaining(), "toJSON").toEqual [attributes[1]]
jasmine-jquery
• a set of custom matchers for jQuery framework
• an API for handling HTML, CSS, and JSON fixtures in your specs
expect($('<div id="some-id"></div>')).toBe('div#some-id')
expect($('<input type="checkbox" checked="checked"/>')).toBeChecked()
expect($('<div style="display: none; margin: 10px;"></div>')).toHaveCss({display: "none", margin: "10px"})
expect($('<option selected="selected"></option>')).toBeSelected()
expect($('<div><span class="some-class"></span></div>')).toContain('span.some-class')
expect($('<div class="some-class"></div>')).toHaveClass("some-class")
expect($('<div><span></span></div>')).toHaveHtml('<span></span>')
expect($('<div><ul></ul><h1>header</h1></div>')).toContainHtml('<ul></ul>')
expect($('<div>some text</div>')).toHaveText('some text')
expect($('<input type="text" value="some text"/>')).toHaveValue('some text')
expect('<input type='submit' disabled='disabled'/>').toBeDisabled()
expect($('<input type='text' />').focus()).toBeFocused()
HTML fixtures
In myfixture.html file:
<div id="my-fixture">some complex content here</div>
Inside your test:
loadFixtures('myfixture.html');$('#my-fixture').myTestedPlugin();expect($('#my-fixture')).to...;
jasmine-fixture// Let's say you want to write a Jasmine spec for some code// that needs to select elements from the DOM with jQuery:
$('#toddler .hidden.toy input[name="toyName"][value="cuddle bunny"]')
// In the good ol' days of manually crafting your HTML fixtures,// you'd have to append some raw HTML to the DOM like this:
beforeEach(function(){ $('<div id="toddler"><div class="hidden toy"><input name="toyName" value="cuddle bunny"></div></div>').appendTo('body');});
afterEach(function(){ $('#toddler').remove()});
// But jasmine-fixture's affix method lets you do this instead:
beforeEach(function(){ affix('#toddler .hidden.toy input[name="toyName"][value="cuddle bunny"]')});
jasmine-givendescribe("assigning stuff to this", function() { Given(function() { this.number = 24; }); Given(function() { this.number++; }); When(function() { this.number *= 2; }); Then(function() { return this.number === 50; }); // or Then(function() { expect(this.number).toBe(50) });});
describe("assigning stuff to variables", function() { var subject; Given(function() { subject = []; }); When(function() { subject.push('foo'); }); Then(function() { return subject.length === 1; }); // or Then(function() { expect(subject.length).toBe(1); });});
jasmine-givendescribe "assigning stuff to this", -> Given -> @number = 24 Given -> @number++ When -> @number *= 2 Then -> @number == 50 # or Then -> expect(@number).toBe(50)
describe "assigning stuff to variables", -> subject=null Given -> subject = [] When -> subject.push('foo') Then -> subject.length == 1 # or Then -> expect(subject.length).toBe(1)
jasmine-stealthdescribe("multiple stubbings", function() { var someSpy; beforeEach(function() { someSpy = jasmine.createSpy(); someSpy.when("pirate", { booty: ["jewels",jasmine.any(String)]}).thenReturn("argh!"); someSpy.when("panda",1).thenReturn("sad"); });
it("stubs the first accurately", function() { expect(someSpy("pirate",{ booty: ["jewels","coins"]})).toBe("argh!"); });
it("stubs the second too", function() { expect(someSpy("panda",1)).toBe("sad"); });
it("doesn't return anything when a stubbing isn't satisfied",function(){ expect(someSpy("anything else at all")).not.toBeDefined(); });});
References
https://github.com/rsim/backbone_coffeescript_demo
http://pivotal.github.io/jasmine/
https://github.com/rsim/rpn_calculator
http://searls.testdouble.com/posts/2013-03-21-jasmine-tactics-screencast.html
http://vimeo.com/53655205