The document discusses test driven development and the JavaScript testing framework Jasmine. It provides an overview of unit testing and test driven development processes. It then describes key aspects of Jasmine such as describing test suites and specifications, using matchers and assertions to test expectations, setting up tests using beforeEach and tearing them down using afterEach, creating custom matchers, spying on functions, and handling asynchronous tests. The document aims to introduce readers to testing JavaScript code using the Jasmine framework.
2. Points to Discuss
• Unit Testing & Test Driven Development
• Debugging JS
• Writing Testable Code
• Designing own testing framework
• Jasmine
• Testing Forms
2https://in.linkedin.com/in/anupsinghpune
3. How do you test your JS?
1. Write your JavaScript code
2. See if it works in your favourite browser
3. Change something + [F5]
4. If it doesn't work repeat #3 until you make it work or you
go crazy...
5. In case you made it work, discover few days/weeks later
that it doesn't work in another browser
3https://in.linkedin.com/in/anupsinghpune
4. I think I'm going crazy...
4https://in.linkedin.com/in/anupsinghpune
5. Unit Testing
• In computer programming, unit testing is a
procedure used to validate that individual
modules or units of source code are working
properly.
• Unit testing is used for
(i) Test Driven Development
(ii) Fixing bugs
(iii) Regression testing
5https://in.linkedin.com/in/anupsinghpune
6. Test Driven Development
• Test-Driven Development (TDD) is a computer
programming technique that involves
repeatedly first writing a test case and then
implementing only the code necessary to pass
the test.
• Test-driven development is a method of
designing software, not merely a method of
testing.
6https://in.linkedin.com/in/anupsinghpune
7. Test Driven Development
• TDD in its simplest form is just this:
– Write your tests
– Watch them fail
– Make them pass
– Refactor
– Repeat
7https://in.linkedin.com/in/anupsinghpune
10. What do you need?
• A Unit Testing framework
• Development Environment
10https://in.linkedin.com/in/anupsinghpune
11. Tools
Firebug - The popular developer extension for Firefox that got the ball rolling.
See http://getfirebug.org/.
IE Developer Tools - Included in Internet Explorer 8 and later.
Opera Dragonfly - Included in Opera 9.5 and newer. Also works with mobile versions of Opera.
WebKit Developer Tools - Introduced in Safari 3, dramatically improved as of Safari 4, and now available in Chrome.
Logging - http://patik.com/blog/complete-cross-browser-console-log/
1. alert()
2. Console.log()
3. Common logging method that for all modern browsers
function log() {
try {
console.log.apply(console, arguments);
} catch (e) {
try {
opera.postError.apply(opera, arguments);
} catch (e) {
alert(Array.prototype.join.call(arguments, " "));
}
}
}
1. Tries to log message using the
most common method
2. Catches any failure in logging
3. Tries to log the Opera way
Uses alert if all else fails
Testing and debugging - Debugging code
12. Breakpoints allow us to halt execution at a specific line of code so we can take a gander at the state.
<!DOCTYPE html>
<html>
<head>
<title>Listing 2.2</title>
<script type="text/javascript" src="log.js"></script>
<script type="text/javascript">
var x = 213;
log(x);
</script>
</head>
<body>
</body>
</html>
Testing and debugging - Breakpoints
https://in.linkedin.com/in/anupsinghpune
13. Good tests make Good code - Emphasis on the word good.
It's quite possible to have an extensive test suite that doesn't really help the quality of our
code, if the tests are poorly constructed.
Good tests exhibit three important characteristics:
1. Repeatability - Our test results should be highly reproducible. Tests run repeatedly should always produce
the exact same results. If test results are nondeterministic, how would we know which results are valid and which
are invalid?
2. Simplicity - Our tests should focus on testing one thing. We should strive to remove as much HTML markup,
CSS, or JavaScript as we can without disrupting the intent of the test case. The more we remove, the greater the
likelihood that the test case will only be influenced by the specific code that we’re testing.
3. Independence - Our tests should execute in isolation. We must avoid making the results from one test
dependent upon another. Breaking tests down into the smallest possible
Test generation
https://in.linkedin.com/in/anupsinghpune
14. A test suite should serve as a fundamental part of your development workflow, so you
should pick a suite that works particularly well for your coding style and your
code
base.
JavaScript unit testing framework features
• The ability to simulate browser behaviour (clicks, keypresses, and so on)
• Interactive control of tests (pausing and resuming tests)
• Handling asynchronous test timeouts
• The ability to filter which tests are to be executed
Testing Frameworks
https://in.linkedin.com/in/anupsinghpune
15. Market Share of Testing frameworks
15https://in.linkedin.com/in/anupsinghpune
16. The fundamentals of a test suite
The fundamentals of a test suite
1. Aggregate all the individual tests into a single unit
2. Run the in Bulk
3. Providing a single resource that can be run easily and repeatedly
How to construct a test suite
Q. Why would I want to build a new test suite, When There are already a number of good-quality suites
to choose from?
A. Building your own test suite can serve as a good learning experience, especially when looking at how
asynchronous testing works.
16https://in.linkedin.com/in/anupsinghpune
17. The Assertion – (assert.html)
17
1. The core of a unit-testing framework is its assertion method, usually named
assert().
2. This takes a value—an expression whose premise is asserted—and a
description that describes the purpose of the assertion. If the value
evaluates to true
3. Either the assertion passes or it’s considered a failure.
4. The associated message is usually logged with an appropriate pass/fail
indicator.
https://in.linkedin.com/in/anupsinghpune
20. Test Groups – (test_group.html)
1. Grouping assertions together in a testing context to form test
groups.
2. Test group will likely represent a collection of assertions as they
relate to a single method in our API or application
3. If any assertion fails, then the entire test group is marked as failing
20https://in.linkedin.com/in/anupsinghpune
21. So what's the first step to sanity?
WRITE TESTABLE CODE
21https://in.linkedin.com/in/anupsinghpune
22. What's wrong with this code?
js_sample_001.js
(inline functions and more inside, ajax
directly hooked to element, etc.)
22https://in.linkedin.com/in/anupsinghpune
32. Now what about testing?
Popular JS Unit-testing frameworks:
QUnit
Jasmine
UnitJS
JsUnit (no longer actively maintained)
Some other – see:
http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#JavaScript
32https://in.linkedin.com/in/anupsinghpune
33. Introducing Jasmine
• Testing framework
• Suites possess a hierarchical structure
• Tests as specifications
• Matchers, both built-in and custom
• Spies, a test double pattern
33https://in.linkedin.com/in/anupsinghpune
34. Jasmine suite
describe("A specification suite", function() {
…
});
• Group specifications together using nested
describe function blocks.
• Also useful for delineating context-specific
specifications.
34https://in.linkedin.com/in/anupsinghpune
35. Jasmine specification
describe("A specification suite", function() {
it(“contains spec with an expectation", function() {
expect(view.tagName).toBe(‘tr’);
});
});
• Specifications are expressed with the it function.
• The description should read well in the report.
• Expectations are expressed with the expect
function
35https://in.linkedin.com/in/anupsinghpune
36. • The describe function, describes a feature of an application. It acts as an
aggregating container for individual tests. You can think of the describe blocks as of
test suites. The describe blocks can be nested inside each other.
• The test itself is located inside the it function. The idea is to exercise one particular
aspect of a feature in one test. A test has a name and a body. Usually the first section
of the test's body calls methods on an object under test while the later one verifies
expected results.
• Code contained in the beforeEach block will get executed before each individual
test. This is a perfect place for any initialization logic that has to be executed before
each test.
• The last things to mention are the expect and the toEqual functions. Using those
two constructs we can compare actual results with the expected ones. Jasmine,
comes with a rich set of matchers, toBeTruthy, toBeDefined, toContain are just
few examples of what is available.
Jasmine specification
36https://in.linkedin.com/in/anupsinghpune
38. Jasmine setup using beforeEach
describe("PintailConsulting.ToDoListView", function() {
var view;
beforeEach(function(){
view = new PintailConsulting.ToDoListView();
});
it(“sets the tagName to ‘div’", function() {
expect(view.tagName).toBe(‘div’);
});
});
38https://in.linkedin.com/in/anupsinghpune
39. Jasmine tear down using afterEach
describe("PintailConsulting.ToDoListView", function() {
var view;
beforeEach(function(){
view = new PintailConsulting.ToDoListView();
});
afterEach(function(){
view = null;
});
it(“sets the tagName to ‘div’", function() {
expect(view.tagName).toBe(‘div’);
});
});
39https://in.linkedin.com/in/anupsinghpune
40. Jasmine custom matchers
beforeEach(function() {
this.addMatchers({
toBeLessThan: function(expected) {
var actual = this.actual;
var notText = this.isNot ? " not" : "";
this.message = function () {
return "Expected " + actual + notText + " to be less than " +
expected;
}
return actual < expected;
}
});
});
40https://in.linkedin.com/in/anupsinghpune
41. Jasmine spies
• Test double pattern.
• Interception-based test double mechanism provided by the
Jasmine library.
• Spies record invocations and invocation parameters, allowing you
to inspect the spy after exercising the SUT.
• Very similar to mock objects.
• More information at
https://github.com/pivotal/jasmine/wiki/Spies.
41https://in.linkedin.com/in/anupsinghpune
42. Jasmine spy usage
Spying and verifying invocation
var spy = spyOn(dependency, “render”);
systemUnderTest.display();
expect(spy).toHaveBeenCalled();
Spying, verifying invocation and argument(s)
var spy = spyOn(dependency, “render”);
systemUnderTest.display(“Hello”);
expect(spy).toHaveBeenCalledWith(“Hello”);
42https://in.linkedin.com/in/anupsinghpune
43. Jasmine spy usage
Spying, verifying number of invocations and
arguments for each call
var spy = spyOn(Leaflet, “circle”).andCallThrough();
mapView.processResults(earthquakeJsonResults);
expect(spy).toHaveBeenCalled()
expect(circleConstructorSpy.callCount).toBe(2);
expect(circleConstructorSpy.argsForCall[0][0]).toEqual([56.681
2, -155.0237])
43https://in.linkedin.com/in/anupsinghpune
44. Loose matching with jasmine.any
• Accepts a constructor or “class” name as an expected
value.
• Returns true if the constructor matches the constructor of
the actual value.
var spy = jasmine.createSpy(My.Namespace, ’foo’);
foo(12, function(x) { return x * x; });
expect(spy).toHaveBeenCalledWith
(jasmine.any(Number), jasmine.any(Function));
44https://in.linkedin.com/in/anupsinghpune
45. Jasmine spy usage
• andCallThrough(): Allows the invocation to
passthrough to the real subject.
• andReturn(result): Return a hard-coded result.
• andCallFake(fakeImplFunction): Return a
dynamically generated result from a function.
• createSpy(identity): Manually create a spy.
• createSpyObj(identity, propertiesArray): Creates a
mock with multiple property spies.
45https://in.linkedin.com/in/anupsinghpune
46. Jasmine asynchronous support
• Use runs and waitsFor blocks and a latch function.
• The latch function polls until it returns true or the
timeout expires, whichever comes first.
• If the timeout expires, the specification fails with a
message.
• Kind of clunky to use.
46https://in.linkedin.com/in/anupsinghpune
47. Jasmine asynchronous example
describe("an async spec", function() {
var done;
beforeEach(function() {
done = false;
var doStuff = function() {
// simulate async stuff and wait 10ms
setTimeout(function() { done = true; }, 10);
};
runs(doStuff);
waitsFor(function() { return done; }, ‘The doStuff function should be done by
now.’, 100);
});
it("did stuff", function() {
expect(done).toBe(true);
});
});
47https://in.linkedin.com/in/anupsinghpune