- Senior UI Developer at Hobsons, where we build cool stuff to help teachers, students, and school counselors
- Author of an upcoming book on Jasmine
- Publisher of A Drip of JavaScript, a weekly JavaScript newsletter
Like...
Raise your hands for yes.
describe("Addition function", function() {
it("should add numbers", function() {
expect(add(1, 1)).toBe(2);
});
});
The practice of writing your tests first, so that they can guide your implementation.
The steps of doing TDD are:
They're the same thing.
Jasmine uses the term "specs" to emphasize that they should be written before you start coding.
describe block (they can be nested)it block which contains a specexpect which is passed an expression (called the "actual" value)toBe and toBeGreaterThan)
describe("Calculator", function() {
describe("Addition function", function() {
it("should add numbers", function() {
expect(add(1, 1)).toBe(2);
expect(add(2, 2)).toBeGreaterThan(3);
});
});
});
Is just an ordinary HTML file that you load in your browser.
Include the Jasmine library, your sources, your specs, and you have a test environment.
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css"> <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script> <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script> <!-- include source files here... --> <script type="text/javascript" src="src/Player.js"></script> <script type="text/javascript" src="src/Song.js"></script> <!-- include spec files here... --> <script type="text/javascript" src="spec/SpecHelper.js"></script> <script type="text/javascript" src="spec/PlayerSpec.js"></script>
We're going to write a mini string library which has the following functions.
n of a string.stringUtil library.firstWord function.
describe("stringUtil", function() {
describe("firstWord", function() {
// Specs go here.
});
});
describe("stringUtil", function() {
describe("firstWord", function() {
it("should return the first word of a string", function () {
expect(stringUtil.firstWord("one two")).toBe("one");
});
});
});
stringUtil isn't defined.
var stringUtil = {};
stringUtil doesn't have a firstWord method
var stringUtil = {
firstWord: function() {
// Not doing anything yet.
}
};
var stringUtil = {
firstWord: function(text) {
var textWords = text.split(" ");
return textWords[0];
}
};
var stringUtil = {
firstWord: function(text) {
return text.split(" ")[0];
}
};
nthWord
describe("stringUtil", function() {
describe("firstWord", function() {
it("should return the first word of a string", function () {
expect(stringUtil.firstWord("one two")).toBe("one");
});
});
describe("nthWord", function() {
it("should return the nth word of a string", function () {
expect(stringUtil.nthWord("one two", 1)).toBe("one");
expect(stringUtil.nthWord("one two", 2)).toBe("two");
});
});
});
stringUtil doesn't have a nthWord method
nthWord
var stringUtil = {
firstWord: function(text) {
return text.split(" ")[0];
},
nthWord: function(text, i) {
return text.split(" ")[i - 1];
}
};
nthWord method is so simple that there isn't really anything to refactor in the method itself.
var stringUtil = {
firstWord: function(text) {
return text.split(" ")[0];
},
nthWord: function(text, i) {
return text.split(" ")[i - 1];
}
};
firstWord
describe("firstWord", function() {
it("should return the first word of a string", function () {
expect(stringUtil.firstWord("one two")).toBe("one");
});
it("should delegate its logic to nthWord", function () {
spyOn(stringUtil, "nthWord");
stringUtil.firstWord("one two");
expect(stringUtil.nthWord).toHaveBeenCalled();
});
});
Jasmine spies are like double-agents. They replace an ordinary function and report back what they're doing.
They can:
nthWord isn't being called
firstWord to delegate to nthWord
var stringUtil = {
firstWord: function(text) {
return this.nthWord(text, 1);
},
nthWord: function(text, i) {
return text.split(" ")[i - 1];
}
};
var stringUtil = {
firstWord: function(text) {
return this.nthWord(text, 1);
},
nthWord: function(text, i) {
return text.split(" ")[i - 1];
}
};
| Matcher | Description |
|---|---|
| toBe | Compares actual and expected with === |
| toEqual | Compares simple object literals that === cannot |
| toMatch | Compares actual value against a regular expression |
| toBeDefined | Checks to see if the actual value is defined |
| toBeUndefined | Checks to see if the actual value is undefined |
| toBeNull | Checks to see if the actual value is null |
| toBeTruthy | Checks to see if the actual value coerces to true |
| Matcher | Description |
|---|---|
| toBeFalsy | Checks to see if the actual value coerces to false |
| toContain | Checks to see if an array contains the expected value |
| toBeLessThan | Self-explanatory |
| toBeGreaterThan | Self-explanatory |
| toBeCloseTo | For precision math comparisons on floating point numbers |
| toThrow | Checks to see if an error is thrown |
| toHaveBeenCalled | Checks to see if the spy was called |
| toHaveBeenCalledWith | Checks to see if the spy was called with the expected parameters |
This example checks the last element in an array to see if it matches the expected value.
this.addMatchers({
toEndWith: function(expected) {
return this.actual[this.actual.length - 1] === expected;
}
});
expect(red).toBe(jasmine.any(Color));
describe("stringUtil", function() {
describe("firstWord", function() {
it("should return the first word of a string", function () {
expect(stringUtil.firstWord("one two")).toBe("one");
});
it("should delegate its logic to nthWord", function () {
spyOn(stringUtil, "nthWord");
stringUtil.firstWord("one two");
expect(stringUtil.nthWord).toHaveBeenCalled();
});
});
describe("nthWord", function() {
it("should return the nth word of a string", function () {
expect(stringUtil.nthWord("one two", 1)).toBe("one");
expect(stringUtil.nthWord("one two", 2)).toBe("two");
});
});
});
describe "stringUtil", ->
describe "firstWord", ->
it "should return the first word of a string", ->
expect(stringUtil.firstWord("one two")).toBe "one"
it "should delegate its logic to nthWord", ->
spyOn stringUtil, "nthWord"
stringUtil.firstWord "one two"
expect(stringUtil.nthWord).toHaveBeenCalled()
describe "nthWord", ->
it "should return the nth word of a string", ->
expect(stringUtil.nthWord("one two", 1)).toBe "one"
expect(stringUtil.nthWord("one two", 2)).toBe "two"
CoffeeScript specs end up being a lot cleaner and easier to read.
It depends on:
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-contrib-jasmine");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.initConfig({
jasmine: {
test: {
src: ["lib/jquery.min.js", "src/mySource.js"],
options: {
specs: "spec/mySpec.js"
}
}
},
watch: {
files: ["src/mySource.js", "spec/mySpec.coffee"],
tasks: ["default"]
}
});
grunt.registerTask("default", ["jasmine:test"]);
};
novawebdev. It will be good for the next month.You can find it at: adripofjavascript.com
You can find them at: joshuacc.github.com/tdd-with-jasmine/
Source code at: github.com/joshuacc/tdd-with-jasmine/
Feel free to use them to present at your company.
A link back would be nice. :-)