Unit Tests verify that a relatively small piece of code is doing what it is intended to do. They are narrow in scope and do not check outside systems.
This workshop focuses on Unit Tests
Integration Tests – demonstrate that different pieces of the system work together.
QA Tests – product-oriented tests that focus on identifying "bugs" in software from the perspective of users. May be automated or manual.
Regression Tests – product-oriented tests that make sure that new changes do not break mission-critical functionality that has already been built (logging in/out, checkout out).
You may be familiar with the following steps:
This can be useful when you're trying to learn the basics, move quickly, and prototype.
Sometimes, it can feel "slower" to write tests.
Projects can live a long time
Code bases can get big
Developers come and go
Time required for manually testing adds up
"upgrades" and "bug fixes" can break other features
window.bees = 200;
function collectHoney(honeyBucket){ // input honeyBucket
window.bees = 0; // side effect occurs outside function
return honeyBucket++; // output
};
Test-driven Development (TDD) is an approach to programming.
TDD recommends tests first before any code.
But it's good to remember the reason: maintainable, understandable, reliable code.
The most important thing is that you write tests – regardless of whether you choose to do so before, during, or after coding.
Behavior-driven Development (BDD) emerged out of TDD as an approach to programming that considers both business and technical needs.
From the developer perspective, BDD is a style of TDD that uses English in a natural way to describe expected behavior.
In this workshop, we will be using the following pattern:
Describe "a part of an application"
It "should do something specific"
So you might have something like this:
Describe "a cat trampoline"
It "should have a place for a cat to jump"
It "should not be large enough for a person"
It "should make cats happy"
Suggested time: 10 minutes
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks.
A spec is short for "specification". This is a building block in Jasmine that describes a certain behavior precisely
A suite is a grouping of specs
In this workshop, we'll be running our tests in a browser from a file called SpecRunner.html
. That means, we need to make sure our files are loaded before we write tests. Here's how:
<!-- include jasmine library -->
<link rel="stylesheet" type="text/css" href="lib/jasmine.css">
<script type="text/javascript" src="lib/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-html.js"></script>
<script type="text/javascript" src="lib/boot.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="src/App.js"></script>
<script type="text/javascript" src="src/Game.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/GameSpec.js"></script>
// Suites start with the keyword "describe"
describe("A suite", function(){
// Specs start with the keyword "it"
it("contains spec with an expectation", function(){
// expect() statements have matchers like toBe()
expect(true).toBe(true);
});
});
toBe()
or not.toBe()
There are special functions called matchers that will compare an actual value with an expected value.
You can also add a .not()
to your matcher:
expect(true).not.toBe(false);
Here are some other matchers that are built-in:
.toBeDefined();
.toEqual(); // strict equality
.toMatch(); // for regular expressions
.toContain(); // find an item in an array
Often, you'll run tests with the same set up and tear down procedures. For that, we can use the beforeEach()
and afterEach()
functions:
describe("a self-driving car", function(){
describe("turning in traffic", function(){
beforeEach(function(){
startEngine();
navigate("Safeway");
});
afterEach(function(){
navigate("Home");
killEngine();
});
it("should signal when turning", function(){
turnLeft();
expect(leftSignalPosition).toEqual("on");
});
});
});
Suggested time: 30 minutes
Control Flow is the order in which the individual statements, instructions or function calls are executed or evaluated.
Describe "a book collection"
It should sort based on the "sort by" field
It should sort by author's last name if "sort by" field is empty
It should call sort whenever a user selects "filter by"
It should re-render the collection after sort
Control flow can get complicated, and it becomes tricky to test all the possible scenarios manually.
Therefore, it's useful to break up the pieces and write unit tests for each piece.
A stub is a piece of code used to stand in for some other functionality
Spies can test the control flow of an application by:
toHaveBeenCalled()
, toHaveBeenCalledWith()
, and more!describe("a robot sidewalk sweeper", function(){
it("should look left before crossing the street", function(){
spyOn(robot, 'lookLeft'); // set up the spy
robot.crossStreet(); // execute
// use matchers to see if the function was called
expect(robot.lookLeft).toHaveBeenCalled();
expect(robot.lookLeft.calls.count()).toBeGreaterThan(1);
});
});
Function calls to spies are tracked through the calls
property.
expect(robot.lookLeft.calls.count()).toBeGreaterThan(1);
Suggested time: 45 minutes
If you find yourself wanting to use a specific, known set of data repeatedly (especially across different tests), you may want to try using a fixture.
A fixture is a way to consistently represent and load data for your tests. You'll need to find a plugin to use with Jasmine.