diff --git a/README.markdown b/README.markdown index c28165e..37cbc64 100644 --- a/README.markdown +++ b/README.markdown @@ -27,29 +27,66 @@ Exciting changes are afoot and many syntax changes have been made to make Jasmin ### Specs -Each spec is, naturally, a JavaScript function. You tell Jasmine about this spec with a call to `it()` with a name and the function. The string is a description that will be helpful to you when reading a report. +Each spec is, naturally, a JavaScript function. You tell Jasmine about this spec with a call to `it()` with a string and the function. The string is a description that will be helpful to you when reading a report. it('should be a test', function () { var foo = 0 - foo++; + foo++; }); ### Expectations -Within your spec you will want/need to make expectations. These are made like this: +Within your spec you will want/need to make expectations. These are made with the `expect()` funciton and expectation matchers. like this: it('should be a test', function () { - var foo = 0 - foo++; + var foo = 0 + foo++; - expect(foo).toEqual(1); + expect(foo).toEqual(1); }); Results of the expectations are logged for later for reporting. +#### Expectation Matchers + +Jasmine has several built-in matchers. Here are a few: + +`toEqual()` compares objects or primitives and returns true if they are equal + +`toNotEqual()` compares objects or primitives and returns true if they are not equal + +`toMatch()` takes a regex or a string and returns true if it matches + +`toNotMatch()` takes a regex or a string and returns true if it does not match + +`toBeDefined()` returns true if the object or primitive is not `undefined` + +`toBeNull()` returns true if the object or primitive is not `null` + +`toBeTruthy()` returns true if the object or primitive evaluates to true + +`toBeFalsy()` returns true if the object or primitive evaluates to false + +`wasNotCalledWith()` returns true if the object is a spy and was called with the passed arguments + +`toContain()` returns true if an array or string contains the passed variable. + +`toNotContain()` returns true if an array or string does not contain the passed variable. + +#### Writing New Matchers + +A Matcher has a method name, takes an expected value as it's only parameter, has access to the actual value in this, and then makes a call to this.report with true/false with a failure message. Here's the definition of `toEqual()`: + + Jasmine.Matchers.prototype.toEqual = function (expected) { + return this.report((this.actual === expected), + 'Expected ' + expected + ' but got ' + this.actual + '.'); + }); + +Feel free to define your own matcher as needed in your code. If you'd like to add Matchers to Jasmine, please write tests. + ### Asynchronous Specs -You may be asking yourself, "Self, how do I make asynchronous tests?" +You may be thinking, "That's all well and good, but you mentioned something about asynchronous tests." Well, say you need to make a call that is asynchronous - an AJAX API, or some other JavaScript library. That is, the call returns immediately, yet you want to make expectations 'at some point in the future' after some magic happens in the background. @@ -57,22 +94,21 @@ Jasmine allows you to do this with `runs()` and `waits()` blocks. `runs()` blocks by themselves simply run as if they were called directly. The following snippets of code should provide similar results: - it('should be a test', function () { - var foo = 0 - foo++; + var foo = 0 + foo++; - expect(foo).toEqual(1); + expect(foo).toEqual(1); }); and it('should be a test', function () { runs( function () { - var foo = 0 - foo++; + var foo = 0 + foo++; - expect(foo).toEqual(1); + expect(foo).toEqual(1); }); }); @@ -80,38 +116,38 @@ multiple `runs()` blocks in a spec will run serially. For example, it('should be a test', function () { runs( function () { - var foo = 0 - foo++; + var foo = 0 + foo++; - expect(foo).toEqual(1); + expect(foo).toEqual(1); }); runs( function () { - var bar = 0 - bar++; + var bar = 0 + bar++; - expect(bar).toEqual(1); + expect(bar).toEqual(1); }); }); -`runs()` blocks share functional scope -- `this` properties will be common to all blocks, but simple `vars` will not! +`runs()` blocks share functional scope -- `this` properties will be common to all blocks, but declared `var`'s will not! it('should be a test', function () { runs( function () { - this.foo = 0 - this.foo++; + this.foo = 0 + this.foo++; var bar = 0; bar++; - expect(this.foo).toEqual(1); - expect(bar).toEqual(1); + expect(this.foo).toEqual(1); + expect(bar).toEqual(1); }); runs( function () { this.foo++; - var bar = 0 - bar++; + var bar = 0 + bar++; - expect(foo).toEqual(2); - expect(bar).toEqual(1); + expect(foo).toEqual(2); + expect(bar).toEqual(1); }); }); @@ -119,23 +155,23 @@ multiple `runs()` blocks in a spec will run serially. For example, timeout before the next block is run. You supply a time to wait before the next `runs()` function is executed. For example: it('should be a test', function () { - runs(function () { - this.foo = 0; - var that = this; - setTimeout(function () { - that.foo++; - }, 250); - }); + runs(function () { + this.foo = 0; + var that = this; + setTimeout(function () { + that.foo++; + }, 250); + }); - runs(function () { - this.expects_that(this.foo).should_equal(0); - }); + runs(function () { + this.expects_that(this.foo).should_equal(0); + }); - waits(500); + waits(500); - runs(function () { - this.expects_that(this.foo).should_equal(1); - }); + runs(function () { + this.expects_that(this.foo).should_equal(1); + }); }); What's happening here? @@ -160,9 +196,119 @@ Specs are grouped in Suites. Suites are defined using the global `describe()` f }); }); -The name is so that reporting is more descriptive. +The Suite name is so that reporting is more descriptive. -Suites are executed in the order in which `describe()` calls are made, usually in the order in which their script files are included. +Suites are executed in the order in which `describe()` calls are made, usually in the order in which their script files are included. Additionally, specs within a suite share a functional scope. So you may declare variables inside a describe block and they are accessible from within your specs. For example: + + describe('A suite with some variables', function () { + var bar = 0 + + it('has a test', function () { + bar++; + expect(bar).toEqual(1); + }); + + it('has another test', function () { + bar++; + expect(bar).toEqual(2); + }); + }); + +#### beforeEach + +A suite can have a beforeEach declaration. It takes a function that is run before each spec. For example: + + describe('some suite', function () { + + var suiteWideFoo; + + beforeEach(function () { + suiteWideFoo = 1; + } + + it('should equal bar', function () { + expect(suiteWideFoo).toEqual(1); + }; + }); + +#### afterEach + +Similarly, there is an afterEach declaration. It takes a function that is run after each spec. For example: + + describe('some suite', function () { + + var suiteWideFoo; + afterEach(function () { + suiteWideFoo = 0; + } + + it('should equal 1', function () { + expect(suiteWideFoo).toEqual(1); + }; + + it('should equal 0 after', function () { + expect(suiteWideFoo).toEqual(0); + }; + }); + +### Spies + +Jasmine integrates 'spies' that permit many spying, mocking, and faking behaviors. + +Here are a few examples: + + var Klass = function () { + } + + var Klass.prototype.method = function (arg) { + return arg; + } + + var Klass.prototype.methodWithCallback = function (callback) { + return callback('foo'); + } + + ... + + it('should spy on Klass#method') { + spyOn(Klass, 'method'); + Klass.method('foo argument'); + + expect(Klass.method).wasCalledWith('foo argument'); + }); + + it('should spy on Klass#methodWithCallback') { + var callback = Jasmine.createSpy(); + Klass.method(callback); + + expect(callback).wasCalledWith('foo'); + }); + +There are spy-specfic matchers that are very handy. + +`wasCalled()` returns true if the object is a spy and was called + +`wasCalledWith(arguments)` returns true if the object is a spy and was called with the passed arguments + +`wasNotCalled()` returns true if the object is a spy and was not called + +`wasNotCalledWith(arguments)` returns true if the object is a spy and was not called with the passed arguments + +`andCallThrough()`: spies on AND calls the original function spied on + +`andReturn(arguments)`: returns passed arguments when spy is called + +`andThrow(exception)`: throws passed exception when spy is called + +`andCallFake(function)`: calls passed function when spy is called + +`callCount`: returns number of times spy was called + +`mostRecentCall.args`: returns argument array from last call to spy. + +`argsForCall[i]` returns arguments array for call `i` to spy. + +Spies are automatically removed after each spec. They may be set in the beforeEach function. ### Runner @@ -194,145 +340,18 @@ Here's the example HTML file (in `jasmine/example`): +It's the call to `jasmine.execute()` that runs all of the defined specs, gathering reports of each expectation. + ### Reports If a reporter exists on the Jasmine instance (named `jasmine`), it will be called when each spec, suite and the overall runner complete. If you're at the single-spec result level, you'll get a spec description, whether it passed or failed, and what the failure message was. At the suite & runner report level, you'll get the total specs run so far, the passed counts, failed counts, and a description (of the suite or runner). There is a `Jasmine.Reporters` namespace for you to see how to handle reporting. See the file `json_reporter.js`, which takes the results objects and turns them into JSON strings, for two examples of how to make the results callbacks work for you. -### Custom Matchers - -Jasmine has several matchers: - -`toEqual` compares objects or primitives and returns true if they are equal - -`toNotEqual` compares objects or primitives and returns true if they are not equal - -`toMatch` takes a regex or a string and returns true if it matches - -`toNotMatch` takes a regex or a string and returns true if it does not match - -`toBeDefined` returns true if the object or primitive is not `undefined` - -`toBeNull` returns true if the object or primitive is not `null` - -`toBeTruthy` returns true if the object or primitive evaluates to true - -`toBeFalsy` returns true if the object or primitive evaluates to false - -`wasCalled` returns true if the object is a spy and was called - -`wasNotCalled` returns true if the object is a spy and was not called - -`wasNotCalledWith` returns true if the object is a spy and was called with the passed arguments - -`toContain` returns true if an array or string contains the passed variable. - -`toNotContain` returns true if an array or string does not contain the passed variable. - -### Writing new Matchers -A Matcher has a method name, takes an expected value as it's only parameter, has access to the actual value in this, and then makes a call to this.report with true/false with a failure message. Here's the definition of should\_equal(): - - Jasmine.Matchers.prototype.shouldEqual = function (expected) { - return this.report((this.actual === expected), - 'Expected ' + expected + ' but got ' + this.actual + '.'); - }); - -Feel free to define your own matcher as needed in your code. If you'd like to add Matchers to Jasmine, please write tests. ### Disabling Tests & Suites -specs may be disabled by calling `xit()` instead of `it()`. -suites may be disabled by calling `xdescribe()` instead of `describe()` - -### beforeEach - -beforeEach takes a function that is run before each spec. For example: - - describe('some suite', function () { - - var suiteWideFoo; - beforeEach(function () { - suiteWideFoo = 1; - } - - it('should equal bar', function () { - expect(suiteWideFoo).toEqual(1); - }; - }); - -### afterEach - -afterEach takes a function that is run after each spec. For example: - - describe('some suite', function () { - - var suiteWideFoo; - afterEach(function () { - suiteWideFoo = 0; - } - - it('should equal 1', function () { - expect(suiteWideFoo).toEqual(1); - }; - - it('should equal 0 after', function () { - expect(suiteWideFoo).toEqual(0); - }; - }); - - -### Spies - -Jasmine integrates 'spies' that permit many spying, mocking, and faking behaviors. - -Here are a few examples: - - var Klass = function () { - } - - var Klass.prototype.method = function (arg) { - return arg; - } - - var Klass.prototype.methodWithCallback = function (callback) { - return callback('foo'); - } - - -... - - it('should spy on Klass#method') { - spyOn(Klass, 'method'); - Klass.method('foo argument'); - expect(Klass.method).wasCalledWith('foo argument'); - }); - - it('should spy on Klass#methodWithCallback') { - var callback = Jasmine.createSpy(); - Klass.method(callback); - expect(callback).wasCalledWith('foo'); - }); - -Many other options are available for spies: - -`andCallThrough()`: spies on AND calls the original function spied on - -`andReturn()`: returns passed arguments when spy is called - -`andThrow()`: throws passed exception when spy is called - -`andCallFake()`: calls passed function when spy is called - -`callCount`: returns number of times spy was called - -`mostRecentCall.args`: returns argument array from last call to spy. - -`argsForCall[i]` returns arguments array for call `i` to spy. - -Spies are automatically removed after each spec. They may be set in the beforeEach function. - -Big thanks to [Christian Williams](xian@pivotallabs.com) are due for his addition of spy code. +Specs may be disabled by calling `xit()` instead of `it()`. Suites may be disabled by calling `xdescribe()` instead of `describe()`. A simple find/replace in your editor of choice will allow you to run a subset of your specs. Contributing and Tests ---------------------- @@ -343,6 +362,12 @@ So we made a little bootstrappy test reporter that lets us test Jasmine's pieces Your contributions are welcome. Please submit tests with your pull request. +## Support +We now have a Google Group for support & discussion. + +* Homepage: [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js) +* Group email: [jasmine-js@googlegroups.com](jasmine-js@googlegroups.com) + ## Maintainers * [Davis W. Frank](dwfrank@pivotallabs.com), Pivotal Labs @@ -351,8 +376,9 @@ Your contributions are welcome. Please submit tests with your pull request. ## Acknowledgments * A big shout out to the various JavaScript test framework authors, especially TJ for [JSpec](http://github.com/visionmedia/jspec/tree/master) - we played with it a bit before deciding that we really needed to roll our own. * Thanks to Pivot [Jessica Miller](http://www.jessicamillerworks.com/) for our fancy pass/fail/pending icons -* Huge contributions have been made by [Christian Williams](xian@pivotallabs.com), [Erik Hanson](erik@pivotallabs.com), [Adam Abrons](adam@pivotallabs.com) and [Carl Jackson](carl@pivotallabs.com), and many other Pivots. +* Huge contributions have been made by [Christian Williams](xian@pivotallabs.com) (the master "spy" coder), [Erik Hanson](erik@pivotallabs.com), [Adam Abrons](adam@pivotallabs.com) and [Carl Jackson](carl@pivotallabs.com), and many other Pivots. ## TODO List * Pending & Disabled counts should be included in results +* BUG: describe blocks can't be empty - specs will stall