Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

added async tests using callback pattern. runsAsync. #135

Closed
wants to merge 2 commits into from

4 participants

@drewlesueur

I think callback-style tests would be a good addition to Jasmine. Jasmine already has async tests but they are based either on waiting a specified time, or continuously polling for a condition to be true.

This change adds a runsAsync method that is modeled after node.js style async functions. You pass runsAsync a function just like runs, but this time it takes a callback parameter. You call callback() when your async function is all done. If the first parameter you call callback with is not null, then the test will fail.

Tests included in commit.

@tf
tf commented

Yes, I have also used an idiom like that a lot in my specs, either calling it runsUntil(function(done) {...}) or waitsForCallback(function(callack) {...}).

@rdingwall

Thanks! I'm just in the process of porting a few hundred tests from QUnit to Jasmine and this has enabled us to proceed. We do not want to go back to polling/timeout async tests :)

I hope this gets pulled soon. There was some discussion on the Jasmine mailing list back in 2009 about this and it looks like they agreed it was a good idea but I guess nothing came of it.

@drewlesueur

Thank you. There is a similar testing framework called Mocha that looks like it has better async features. http://visionmedia.github.com/mocha/

@infews
Owner

Master has a Mocha-inspired "done" callback syntax. Closing.

@infews infews closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 27, 2011
  1. @drewlesueur
Commits on Sep 29, 2011
  1. @drewlesueur
This page is out of date. Refresh to see the latest.
Showing with 221 additions and 0 deletions.
  1. +95 −0 lib/jasmine-core/jasmine.js
  2. +126 −0 spec/core/SpecRunningSpec.js
View
95 lib/jasmine-core/jasmine.js
@@ -510,6 +510,16 @@ var runs = function(func) {
if (isCommonJS) exports.runs = runs;
/**
+ * Defines part of a jasmine spec to be ran asynchronously. Will not execute more than one test at once, but will wait until this test is done before continuing.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runsAsync = function(func) {
+ jasmine.getEnv().currentSpec.runsAsync(func);
+};
+if (isCommonJS) exports.runsAsync = runsAsync;
+
+/**
* Waits a fixed time period before moving to the next block.
*
* @deprecated Use waitsFor() instead
@@ -1004,6 +1014,60 @@ jasmine.Block.prototype.execute = function(onComplete) {
}
onComplete();
};
+
+
+/**
+ * BlockAsyncs are functions with async executable code that make up a spec.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} func The function to run asyncrononously. Follows the node.js convtion of error, callback parameters.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.BlockAsync = function(env, timeout, func, message, spec) {
+ this.timeout = timeout || env.defaultTimeoutInterval;
+ this.func = func;
+ this.message = message;
+ this.type = "BlockAsync"
+ jasmine.Block.call(this, env, func, spec);
+};
+
+jasmine.BlockAsync.prototype.execute = function(onComplete) {
+ if (jasmine.VERBOSE) {
+ this.env.reporter.log('>> Jasmine waiting asynchronously for' + (this.message || 'something to happen'));
+ }
+ var failTimer;
+ try {
+ var that = this
+ failTimer = this.env.setTimeout(function() {
+ var message = 'timed out after ' + that.timeout + ' msec waiting for ' + (that.message || 'something to happen');
+ that.spec.fail({
+ name: 'timeout',
+ message: message
+ });
+ that.abort = true;
+ onComplete();
+ }, this.timeout);
+
+ var that = this
+ this.func.apply(this.spec, [function(err) {
+ that.env.clearTimeout(failTimer);
+ if (err) {
+ that.spec.fail(err);
+ } else {
+ onComplete();
+ }
+ }]);
+
+ } catch (e) {
+ this.spec.fail(e);
+ }
+};
+
+
/** JavaScript API reporter.
*
* @constructor
@@ -1945,6 +2009,37 @@ jasmine.Spec.prototype.runs = function (func) {
return this;
};
+/**
+ * Runs an async function. Waits for callback to be called before continuing
+ *
+ * @param {Function} func
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.runsAsync = function(func, optional_timeoutMessage, optional_timeout) {
+ var func_ = null;
+ var optional_timeoutMessage_ = null;
+ var optional_timeout_ = null;
+ for (var i = 0; i < arguments.length; i++) {
+ var arg = arguments[i];
+ switch (typeof arg) {
+ case 'function':
+ func_ = arg;
+ break;
+ case 'string':
+ optional_timeoutMessage_ = arg;
+ break;
+ case 'number':
+ optional_timeout_ = arg;
+ break;
+ }
+ }
+
+ var blockAsync = new jasmine.BlockAsync(this.env, optional_timeout_, func_, optional_timeoutMessage_, this);
+ this.addToQueue(blockAsync);
+ return this;
+};
+
jasmine.Spec.prototype.addToQueue = function (block) {
if (this.queue.isRunning()) {
this.queue.insertNext(block);
View
126 spec/core/SpecRunningSpec.js
@@ -258,6 +258,132 @@ describe("jasmine spec running", function () {
expect(another_spec.results().getItems()[0].passed()).toEqual(true);
});
+
+ it("should run asynchronous tests with callbacks", function () {
+ var foo = 0;
+ foo = 0;
+ env.describe('test async spec', function() {
+ a_spec = env.it('spec w/ queued statments', function () {
+ this.runsAsync(function (callback) {
+ fakeTimer.setTimeout(function() {
+ foo++;
+ callback()
+ }, 500);
+ });
+ var that = this
+ this.runsAsync(function(callback) {
+ fakeTimer.setTimeout(function(){
+ that.expect(foo).toEqual(1);
+ callback()
+ }, 1000);
+ });
+ });
+ });
+
+ a_spec.execute();
+
+ expect(a_spec.results().getItems().length).toEqual(0);
+
+ fakeTimer.tick(500);
+ expect(a_spec.results().getItems().length).toEqual(0);
+
+ fakeTimer.tick(1000);
+ expect(a_spec.results().getItems().length).toEqual(1);
+
+ expect(a_spec.results().getItems()[0].passed()).toEqual(true); // 'Calling waits(): Queued expectation failed';
+
+ fakeTimer.tick(1000);
+
+
+ var baz = 0;
+ var yet_another_spec;
+ env.describe('test async spec', function() {
+ yet_another_spec = env.it('spec w/ async fail on a callback', function () {
+ this.runsAsync(function (callback) {
+ fakeTimer.setTimeout(function() {
+ baz++;
+ callback("not null") //("first arg not null")
+ }, 250);
+ });
+ var that = this
+ this.runsAsync(function(callback) {
+ fakeTimer.setTimeout(function() {
+ that.expect(baz).toEqual(1);
+ callback()
+ }, 250);
+ });
+ });
+ });
+
+ yet_another_spec.execute();
+ fakeTimer.tick(100);
+ fakeTimer.tick(150);
+ expect(yet_another_spec.results().getItems().length).toEqual(1);
+
+ fakeTimer.tick(250);
+
+ expect(yet_another_spec.results().getItems().length).toEqual(1);
+ expect(yet_another_spec.results().getItems()[0].passed()).toEqual(false);
+
+
+
+ var bazzy = 0;
+ var even_more_spec;
+ env.describe('test async spec', function() {
+ even_more_spec = env.it('spec w/ async fail on last callback', function () {
+ this.runsAsync(function (callback) {
+ fakeTimer.setTimeout(function() {
+ that.expect(bazzy).toEqual(0)
+ bazzy++;
+ callback(null) //("first arg not null")
+ }, 250);
+ });
+ var that = this
+ this.runsAsync(function(callback) {
+ fakeTimer.setTimeout(function() {
+ that.expect(bazzy).toEqual(1);
+ callback("error")
+ }, 250);
+ });
+ });
+ });
+
+ even_more_spec.execute();
+ fakeTimer.tick(100);
+ fakeTimer.tick(150);
+ fakeTimer.tick(250);
+
+ expect(even_more_spec.results().getItems().length).toEqual(3);
+ expect(even_more_spec.results().getItems()[0].passed()).toEqual(true);
+ expect(even_more_spec.results().getItems()[1].passed()).toEqual(true);
+ expect(even_more_spec.results().getItems()[2].passed()).toEqual(false);
+
+
+ var foozy = 0;
+ var mas_spec;
+ env.describe('test async spec', function() {
+ mas_spec = env.it('spec w/ async fail by timeout', function () {
+ var that = this
+ this.runsAsync(function (callback) {
+ fakeTimer.setTimeout(function() {
+ that.expect(foozy).toEqual(0)
+ foozy++;
+ that.expect(foozy).toEqual(1);
+ callback(null) //("first arg not null")
+ }, 6000);
+ }, "timeout to happen", 5000);
+ });
+ });
+
+ mas_spec.execute();
+ fakeTimer.tick(5000);
+
+ expect(mas_spec.results().getItems().length).toEqual(1);
+ expect(mas_spec.results().getItems()[0].passed()).toEqual(false);
+ expect(mas_spec.results().getItems()[0].message).toEqual('timeout: timed out after 5000 msec waiting for timeout to happen');
+ });
+
+
describe("waitsFor", function() {
var latchFunction = function() {
return true;
Something went wrong with that request. Please try again.