introduction.js |
|
---|---|
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests. This guide is running against Jasmine version FILLED IN AT RUNTIME. Standalone DistributionThe releases page has links to download the standalone distribution, which contains everything you need to start running Jasmine.
After downloading a particular version and unzipping, opening Suites:
|
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
|
It's Just FunctionsSince |
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});
|
ExpectationsExpectations are built with the function |
describe("The 'toBe' matcher compares with ===", function() {
|
MatchersEach matcher implements a boolean comparison between the actual value and the expected value. It is responsible for reporting to Jasmine if the expectation is true or false. Jasmine will then pass or fail the spec. |
it("and has a positive case", function() {
expect(true).toBe(true);
});
|
Any matcher can evaluate to a negative assertion by chaining the call to |
it("and can have a negative case", function() {
expect(false).not.toBe(true);
});
});
|
Included MatchersJasmine has a rich set of matchers included. Each is used here - all expectations and specs pass. There is also the ability to write custom matchers for when a project's domain calls for specific assertions that are not included below. |
describe("Included matchers:", function() {
it("The 'toBe' matcher compares with ===", function() {
var a = 12;
var b = a;
expect(a).toBe(b);
expect(a).not.toBe(null);
});
describe("The 'toEqual' matcher", function() {
it("works for simple literals and variables", function() {
var a = 12;
expect(a).toEqual(12);
});
it("should work for objects", function() {
var foo = {
a: 12,
b: 34
};
var bar = {
a: 12,
b: 34
};
expect(foo).toEqual(bar);
});
});
it("The 'toMatch' matcher is for regular expressions", function() {
var message = "foo bar baz";
expect(message).toMatch(/bar/);
expect(message).toMatch("bar");
expect(message).not.toMatch(/quux/);
});
it("The 'toBeDefined' matcher compares against `undefined`", function() {
var a = {
foo: "foo"
};
expect(a.foo).toBeDefined();
expect(a.bar).not.toBeDefined();
});
it("The `toBeUndefined` matcher compares against `undefined`", function() {
var a = {
foo: "foo"
};
expect(a.foo).not.toBeUndefined();
expect(a.bar).toBeUndefined();
});
it("The 'toBeNull' matcher compares against null", function() {
var a = null;
var foo = "foo";
expect(null).toBeNull();
expect(a).toBeNull();
expect(foo).not.toBeNull();
});
it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
var a, foo = "foo";
expect(foo).toBeTruthy();
expect(a).not.toBeTruthy();
});
it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
var a, foo = "foo";
expect(a).toBeFalsy();
expect(foo).not.toBeFalsy();
});
describe("The 'toContain' matcher", function() {
it("works for finding an item in an Array", function() {
var a = ["foo", "bar", "baz"];
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
it("also works for finding a substring", function() {
var a = "foo bar baz";
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
});
it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
var pi = 3.1415926,
e = 2.78;
expect(e).toBeLessThan(pi);
expect(pi).not.toBeLessThan(e);
});
it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
var pi = 3.1415926,
e = 2.78;
expect(pi).toBeGreaterThan(e);
expect(e).not.toBeGreaterThan(pi);
});
it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
var pi = 3.1415926,
e = 2.78;
expect(pi).not.toBeCloseTo(e, 2);
expect(pi).toBeCloseTo(e, 0);
});
it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
var foo = function() {
return 1 + 2;
};
var bar = function() {
return a + 1;
};
var baz = function() {
throw 'what';
};
expect(foo).not.toThrow();
expect(bar).toThrow();
expect(baz).toThrow('what');
});
it("The 'toThrowError' matcher is for testing a specific thrown exception", function() {
var foo = function() {
throw new TypeError("foo bar baz");
};
expect(foo).toThrowError("foo bar baz");
expect(foo).toThrowError(/bar/);
expect(foo).toThrowError(TypeError);
expect(foo).toThrowError(TypeError, "foo bar baz");
});
});
|
Manually failing a spec with
|
describe("A spec using the fail function", function() {
var foo = function(x, callBack) {
if (x) {
callBack();
}
};
it("should not call the callBack", function() {
foo(false, function() {
fail("Callback has been called");
});
});
});
|
Grouping Related Specs with
|
describe("A spec", function() {
it("is just a function, so it can contain any code", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
|
Setup and TeardownTo help a test suite DRY up any duplicated setup and teardown code, Jasmine provides the global |
|
As the name implies, the Here is the same set of specs written a little differently. The variable under test is defined at the top-level scope -- the |
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() {
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);
});
});
|
The However, be careful using |
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
|
The
|
describe("A spec", function() {
beforeEach(function() {
this.foo = 0;
});
it("can use the `this` to share state", function() {
expect(this.foo).toEqual(0);
this.bar = "test pollution?";
});
it("prevents test pollution by having an empty `this` created for the next spec", function() {
expect(this.foo).toEqual(0);
expect(this.bar).toBe(undefined);
});
});
|
Nesting
|
describe("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 SuitesSuites can be disabled with the |
xdescribe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
});
|
Pending SpecsPending specs do not run, but their names will show up in the results as |
describe("Pending specs", function() {
|
Any spec declared with |
xit("can be declared 'xit'", function() {
expect(true).toBe(false);
});
|
Any spec declared without a function body will also be marked pending in results. |
it("can be declared with 'it' but without a function");
|
And if you call the function |
it("can be declared by calling 'pending' in the spec body", function() {
expect(true).toBe(false);
pending('this is why it is pending');
});
});
|
SpiesJasmine has test double functions called spies. A spy can stub any function and tracks calls to it and all arguments. A spy only exists in the |
describe("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 all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
});
|
Spies:
|
describe("A spy, when configured to call through", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, 'getBar').and.callThrough();
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(123);
});
});
|
Spies:
|
describe("A spy, when configured to fake a return value", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, "getBar").and.returnValue(745);
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(745);
});
});
|
Spies:
|
describe("A spy, when configured with an alternate implementation", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
|
If the function being spied on receives arguments that the fake needs, you can get those as well |
spyOn(foo, "getBar").and.callFake(function(arguments, can, be, received) {
return 1001;
});
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(1001);
});
});
|
Spies:
|
describe("A spy, when configured to throw an error", function() {
var foo, bar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, "setBar").and.throwError("quux");
});
it("throws the value", function() {
expect(function() {
foo.setBar(123)
}).toThrowError("quux");
});
});
|
Spies:
|
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar').and.callThrough();
});
it("can call through and then stub in the same spec", function() {
foo.setBar(123);
expect(bar).toEqual(123);
foo.setBar.and.stub();
bar = null;
foo.setBar(123);
expect(bar).toBe(null);
});
});
|
Other tracking propertiesEvery call to a spy is tracked and exposed on the |
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
});
|
|
it("tracks if it was called at all", function() {
expect(foo.setBar.calls.any()).toEqual(false);
foo.setBar();
expect(foo.setBar.calls.any()).toEqual(true);
});
|
|
it("tracks the number of times it was called", function() {
expect(foo.setBar.calls.count()).toEqual(0);
foo.setBar();
foo.setBar();
expect(foo.setBar.calls.count()).toEqual(2);
});
|
|
it("tracks the arguments of each call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.argsFor(0)).toEqual([123]);
expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]);
});
|
|
it("tracks the arguments of all calls", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]);
});
|
|
it("can provide the context and arguments to all calls", function() {
foo.setBar(123);
expect(foo.setBar.calls.all()).toEqual([{object: foo, args: [123], returnValue: undefined}]);
});
|
|
it("has a shortcut to the most recent call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.mostRecent()).toEqual({object: foo, args: [456, "baz"], returnValue: undefined});
});
|
|
it("has a shortcut to the first call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.first()).toEqual({object: foo, args: [123], returnValue: undefined});
});
|
When inspecting the return from |
it("tracks the context", function() {
var spy = jasmine.createSpy('spy');
var baz = {
fn: spy
};
var quux = {
fn: spy
};
baz.fn(123);
quux.fn(456);
expect(spy.calls.first().object).toBe(baz);
expect(spy.calls.mostRecent().object).toBe(quux);
});
|
|
it("can be reset", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.any()).toBe(true);
foo.setBar.calls.reset();
expect(foo.setBar.calls.any()).toBe(false);
});
});
|
Spies:
|
describe("A spy, when created manually", function() {
var whatAmI;
beforeEach(function() {
whatAmI = jasmine.createSpy('whatAmI');
whatAmI("I", "am", "a", "spy");
});
it("is named, which helps in error reporting", function() {
expect(whatAmI.and.identity()).toEqual('whatAmI');
});
it("tracks that the spy was called", function() {
expect(whatAmI).toHaveBeenCalled();
});
it("tracks its number of calls", function() {
expect(whatAmI.calls.count()).toEqual(1);
});
it("tracks all the arguments of its calls", function() {
expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
});
it("allows access to the most recent call", function() {
expect(whatAmI.calls.mostRecent().args[0]).toEqual("I");
});
});
|
Spies:
|
describe("Multiple spies, when created manually", function() {
var tape;
beforeEach(function() {
tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
tape.play();
tape.pause();
tape.rewind(0);
});
it("creates spies for each requested function", function() {
expect(tape.play).toBeDefined();
expect(tape.pause).toBeDefined();
expect(tape.stop).toBeDefined();
expect(tape.rewind).toBeDefined();
});
it("tracks that the spies were called", function() {
expect(tape.play).toHaveBeenCalled();
expect(tape.pause).toHaveBeenCalled();
expect(tape.rewind).toHaveBeenCalled();
expect(tape.stop).not.toHaveBeenCalled();
});
it("tracks all the arguments of its calls", function() {
expect(tape.rewind).toHaveBeenCalledWith(0);
});
});
|
Matching Anything with
|
describe("jasmine.any", function() {
it("matches any value", function() {
expect({}).toEqual(jasmine.any(Object));
expect(12).toEqual(jasmine.any(Number));
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var foo = jasmine.createSpy('foo');
foo(12, function() {
return true;
});
expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function));
});
});
});
|
Matching existence with
|
describe("jasmine.anything", function() {
it("matches anything", function() {
expect(1).toEqual(jasmine.anything());
});
describe("when used with a spy", function() {
it("is useful when the argument can be ignored", function() {
var foo = jasmine.createSpy('foo');
foo(12, function() {
return false;
});
expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
});
});
});
|
Partial Matching with
|
describe("jasmine.objectContaining", function() {
var foo;
beforeEach(function() {
foo = {
a: 1,
b: 2,
bar: "baz"
};
});
it("matches objects with the expect key/value pairs", function() {
expect(foo).toEqual(jasmine.objectContaining({
bar: "baz"
}));
expect(foo).not.toEqual(jasmine.objectContaining({
c: 37
}));
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback({
bar: "baz"
});
expect(callback).toHaveBeenCalledWith(jasmine.objectContaining({
bar: "baz"
}));
expect(callback).not.toHaveBeenCalledWith(jasmine.objectContaining({
c: 37
}));
});
});
});
|
Partial Array Matching with
|
describe("jasmine.arrayContaining", function() {
var foo;
beforeEach(function() {
foo = [1, 2, 3, 4];
});
it("matches arrays with some of the values", function() {
expect(foo).toEqual(jasmine.arrayContaining([3, 1]));
expect(foo).not.toEqual(jasmine.arrayContaining([6]));
});
describe("when used with a spy", function() {
it("is useful when comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback([1, 2, 3, 4]);
expect(callback).toHaveBeenCalledWith(jasmine.arrayContaining([4, 2, 3]));
expect(callback).not.toHaveBeenCalledWith(jasmine.arrayContaining([5, 2]));
});
});
});
|
String Matching with
|
describe('jasmine.stringMatching', function() {
it("matches as a regexp", function() {
expect({foo: 'bar'}).toEqual({foo: jasmine.stringMatching(/^bar$/)});
expect({foo: 'foobarbaz'}).toEqual({foo: jasmine.stringMatching('bar')});
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback('foobarbaz');
expect(callback).toHaveBeenCalledWith(jasmine.stringMatching('bar'));
expect(callback).not.toHaveBeenCalledWith(jasmine.stringMatching(/^bar$/));
});
});
});
|
Custom asymmetric equality testerWhen you need to check that something meets a certain criteria, without being strictly equal, you can also specify a custom asymmetric equality tester simply by providing an object that has an |
describe("custom asymmetry", function() {
var tester = {
asymmetricMatch: function(actual) {
var secondValue = actual.split(',')[1];
return secondValue === 'bar';
}
};
it("dives in deep", function() {
expect("foo,bar,baz,quux").toEqual(tester);
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback('foo,bar,baz');
expect(callback).toHaveBeenCalledWith(tester);
});
});
});
|
Jasmine ClockThis syntax has changed for Jasmine 2.0. The Jasmine Clock is available for testing time dependent code. |
describe("Manually ticking the Jasmine Clock", function() {
var timerCallback;
|
It is installed with a call to |
beforeEach(function() {
timerCallback = jasmine.createSpy("timerCallback");
jasmine.clock().install();
});
|
Be sure to uninstall the clock after you are done to restore the original functions. |
afterEach(function() {
jasmine.clock().uninstall();
});
|
Mocking the JavaScript Timeout FunctionsYou can make To execute registered functions, move time forward via the |
it("causes a timeout to be called synchronously", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.clock().tick(101);
expect(timerCallback).toHaveBeenCalled();
});
it("causes an interval to be called synchronously", function() {
setInterval(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.clock().tick(101);
expect(timerCallback.calls.count()).toEqual(1);
jasmine.clock().tick(50);
expect(timerCallback.calls.count()).toEqual(1);
jasmine.clock().tick(50);
expect(timerCallback.calls.count()).toEqual(2);
});
|
Mocking the DateThe Jasmine Clock can also be used to mock the current date. |
describe("Mocking the Date object", function(){
it("mocks the Date object and sets it to a given time", function() {
var baseTime = new Date(2013, 9, 23);
|
If you do not provide a base time to |
jasmine.clock().mockDate(baseTime);
jasmine.clock().tick(50);
expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
});
});
});
|
Asynchronous SupportThis syntax has changed for Jasmine 2.0. Jasmine also has support for running specs that require testing asynchronous operations. |
describe("Asynchronous specs", function() {
var value;
|
Calls to |
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
|
This spec will not start until the |
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
|
By default jasmine will wait for 5 seconds for an asynchronous spec to finish before causing a timeout failure.
If the timeout expires before If specific specs should fail faster or need more time this can be adjusted by passing a timeout value to If the entire suite should have a different timeout, |
describe("long asynchronous specs", function() {
beforeEach(function(done) {
done();
}, 1000);
it("takes a long time", function(done) {
setTimeout(function() {
done();
}, 9000);
}, 10000);
afterEach(function(done) {
done();
}, 1000);
});
|
The |
describe("A spec using done.fail", function() {
var foo = function(x, callBack1, callBack2) {
if (x) {
setTimeout(callBack1, 0);
} else {
setTimeout(callBack2, 0);
}
};
it("should not call the second callBack", function(done) {
foo(true,
done,
function() {
done.fail("Second callback has been called");
}
);
});
});
});
|
Downloads
Support
ThanksRunning documentation inspired by @mjackson and the 2012 Fluent Summit. |
|