Jasmine is a behavior-driven development framework for
testing JavaScript code. It does not depend on any other JavaScript frameworks
1. Suites:
describe Your Tests
A test suite begins with a call to the global Jasmine function describe with two parameters: a string and
a function. The string is a name or title for a spec suite – usually what is
being tested. The function is a block of code that implements the suite.
describe("A suite", function() {
it("contains spec
with an expectation", function() {
expect(true).toBe(true);
});
});
Specs:
A spec contains one or
more expectations that test the state of the code. An expectation in
Jasmine is an assertion that is either true or false. A spec with all true
expectations is a passing spec. A spec with one or more false expectations is a
failing spec.
Since describe and it blocks are functions, they can
contain any executable code necessary to implement the test. JavaScript scoping
rules apply, so variables declared in a describe
are available to any it block inside
the suite.
2.
Expectations:
Expectations
are built with the function expect which takes a value, called the actual. It
is chained with a Matcher function,
which takes the expected value.
Matchers:
Each 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 expect with a not before calling the matcher.
it("and
can have a negative case", function() {
expect(false).not.toBe(true);
});
});
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();
});
it("The 'toContain' matcher is for
finding an item in an Array", 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;
};
expect(foo).not.toThrow();
expect(bar).toThrow();
});
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");
});
});
3. Manually failing a spec with fail:
The fail
function causes a spec to fail. It can take a failure message or an Error
object as a parameter.
escribe("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");
});
});
});
4.
Grouping
Related Specs with describe: ( more about naming convenction)
The describe
function is for grouping related specs. The string parameter is for naming the
collection of specs, and will be concatenated with specs to make a spec’s full
name. This aids in finding specs in a large suite. If you name them well, your
specs read as full sentences in traditional BDD style.
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);
});
});
5. Setup and Teardown: (beforeeach ,aftereach,
beforeall, afterall)
To help a test
suite DRY up any duplicated setup and teardown code, Jasmine provides the
global beforeEach, afterEach, beforeAll, and afterAll functions.
As the name
implies, the beforeEach function is called once before each spec in the
describe in which it is called, and the afterEach function is called once after
each spec.
Here is the
same set of specs written a little differently. The variable under test is
defined at the top-level scope — the describe block — and initialization code
is moved into a beforeEach function.
The afterEach function resets the
variable before continuing.
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 beforeAll function is called only once
before all the specs in describe are run, and the afterAll function is called after all specs finish. These functions
can be used to speed up test suites with expensive setup and teardown.
However, be
careful using beforeAll and afterAll! Since they are not reset between specs,
it is easy to accidentally leak state between your specs so that they
erroneously pass or fail.
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);
});
});
6. The this keyword:
Another way to
share variables between a beforeEach, it, and afterEach is through the this
keyword. Each spec’s beforeEach/it/afterEach has the this as the same empty
object that is set back to empty for the next spec’s beforeEach/it/afterEach.
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);
});
});
7 .
Nesting
describe Blocks
Calls to
describe can be nested, with specs defined at any level. This allows a suite to
be composed as a tree of functions. Before a spec is executed, Jasmine walks
down the tree executing each beforeEach function in order. After the spec is
executed, Jasmine walks through the afterEach functions similarly.
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);
});
});
});
8. Disabling Suites:
Suites can be disabled with the xdescribe function. These
suites and any specs inside them are skipped when run and thus their results
will not appear in the results.
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);
});
});
9. Pending Specs
Pending specs do not run, but their names will show up in
the results as pending.
describe("Pending specs", function() {
Any spec declared with xit is marked as pending.
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 pending anywhere in the spec
body, no matter the expectations, the spec will be marked pending. A string
passed to pending will be treated as a reason and displayed when the suite
finishes.
it("can be
declared by calling 'pending' in the spec body", function() {
expect(true).toBe(false);
pending('this is
why it is pending');
});
});
10. Matching Anything
with jasmine.any
jasmine.any takes a constructor or “class” name as an
expected value. It returns true if the constructor matches the constructor of
the actual value.
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));
});
});
});
11.Matching existence
with jasmine.anything
jasmine.anything returns true if the actual value is not
null or undefined.
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());
});
});
});
12.Partial Matching
with jasmine.objectContaining
jasmine.objectContaining is for those times when an
expectation only cares about certain key/value pairs in the actual.
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
}));
});
});
});
13.Partial Array
Matching with jasmine.arrayContaining
jasmine.arrayContaining is for those times when an
expectation only cares about some of the values in an array.
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]));
});
});
});
14.String Matching
with jasmine.stringMatching
jasmine.stringMatching is for when you don’t want to match a
string in a larger object exactly, or match a portion of a string in a spy
expectation.
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$/));
});
});
});
15. Asynchronous
Support
This 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 beforeAll, afterAll, beforeEach, afterEach, and it
can take an optional single argument that should be called when the async work
is complete.
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
This spec will not start until the done function is called
in the call to beforeEach above. And this spec will not complete until its done
is called.
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 done is called, the current spec will be marked as failed and
suite execution will continue as if done was called.
If specific specs should fail faster or need more time this
can be adjusted by passing a timeout value to it, etc.
If the entire suite should have a different timeout,
jasmine.DEFAULT_TIMEOUT_INTERVAL can be set globally, outside of any given
describe.
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 done.fail function fails the spec and indicates that it
has completed.
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");
}
);
});
});
});
No comments:
Post a Comment