Testing is just validating an expectation about something
Unit Tests, test parts of an application or "units".A unit can be a function, an object, a module, basically everything self-contained that acts like a black box to the outside world. Very commonly, each units is tested individually and independently to ensure an application is running as expected.
Suites are used to describe the test, it is defined with the describe function, which is used to group related Specs. You can think of defining a suite as saying "let me describe ______ to you.". Typically a unit test only contains one describe (suite) at the top of your file.
describe takes two arguments. The first is a string that describes the suite and acts as the name of the suite. The second is a function that acts as the body of the suite where all Specs go into.
You can disable a suite by defining it with xdescribe instead of describe, any spec inside them will be skipped.
Spec is short for "Specification". Specification in terms of a test refer to the technical details of a given feature or application which must be fulfilled. (source: What does “spec” mean in Javascript Testing).
We define a spec with the it function. 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.
Just like describe, it takes two arguments. The first is the a string to describe the spec and acts as the title of the spec. The second is a function that acts as the body of the spec.
Pending specs do not run, but their names will show up in the results as pending. You can put a spec as pending by:
- declaring a spec with
xitinstead ofit, or - declare a spec without a body function, or
- call
pendingfunction anywhere in the body of the spec.
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.
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.
Any matcher can evaluate to a negative assertion by chaining the call to expect with a not before calling the matcher.
Check out the list of matchers in the Jasmine API docs
Jasmine comes with 4 functions to help DRY and setup your code:
beforeEach: called once before each spec in thedescribein which it is called.afterEach(): called once after each spec in thedescribein which it is called.beforeAll: called once before all specs in thedescribein which it is called.afterAll: called once after all specs in thedescribein which it is called finish.
Note: You can share variables between a
beforeEach,it, andafterEachis through thethiskeyword. Each spec's aforementioned functions has thethisas the same empty object that is set back to empty for the next spec'sbeforeEach/it/afterEach.
Putting all the above in one example:
describe("A spec", function() {
beforeEach(function() {
this.foo = 0;
this.foo += 1;
});
afterEach(function() {
this.foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(this.foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(this.foo).toEqual(1);
expect(true).toEqual(true);
});
describe("nested inside a second describe", function() {
beforeEach(function() {
// the same 'this' as the parent suite
// i.e. this.foo is defined and equals 1
this.bar = 1;
});
it("can reference both scopes as needed", function() {
expect(this.foo).toEqual(this.bar);
});
it("this one is be pending");
xit("this one is pending too", function() {
// ...
});
it("and another pending spec", function() {
// ...
});
});
xdescribe("another nested inside a second describe", function () {
// this one is disabled
})
});Note: All these specs pass.
Jasmine has test double functions called spies. A spy can stub (definition of stub) any function and tracks calls to it and all arguments. A spy only exists in the describe or it block in which it is defined, and will be removed after each spec. There are special matchers for interacting with spies.
describe('Spy spec', function() {
beforeEach(function() {
// - we can spy on a function (method) using 'spyOn'.
// - we use 'and' to tell the spy what to do when invoked.
// - we tell the spy to call through to the real implementation
// when invoked i.e. this actually calls the method
spyOn(foo, 'arraySum').and.callThrough();
this.result1 = foo.arraySum([1,2,3,4,5,6,7,8,9]);
this.result2 = foo.arraySum([1,2,3,4,5]);
});
it("should sum array elements together", function() {
// we can check if the function has been called at all
expect(foo.arraySum.calls.any()).toEqual(true);
// we can also do that like this
expect(foo.arraySum).toHaveBeenCalled();
// we can check the times the function has been called
expect(foo.arraySum.calls.count()).toEqual(2);
// we can do that like this as well
expect(foo.arraySum).toHaveBeenCalledTimes(2);
// we can check if it has been called with specific arguments
// Note that the order is not important
expect(foo.arraySum).toHaveBeenCalledWith([1,2,3,4,5]);
expect(foo.arraySum).toHaveBeenCalledWith([1,2,3,4,5,6,7,8,9]);
// and of course we can do other stuff like usual
expect(this.result1).toBe(45);
expect(this.result2).toBe(15);
});
});