So I need to run a Jasmine test based on a specific condition which is returned by a promise, before running the test. Since there doesn't seem to be a way to exclude describes dynamically with Promises, I am checking the promise outcome and then either running some expects or just calling done(). This is not ideal because having a test just with done() results in "SPEC HAS NO EXPECTATIONS", but if I use pending() in the case the test shouldn't be run then the test fails as there is no done() being called within the timeout.
My current implementation is (this function gets called from a describe with a specific testing_case) :
function checkOptions(testing_case) {
for (var key in cases_expectations[testing_case]) {
(function (case, expectation) {
it(case + " should be " + expectation, function (done){
this.is.a.function.then(function(result) {
var current_case = result.case;
if (current_case !== testing_case) {
done();
} else {
this.is.another.function(current).then(function(result) {
expect(result).toBe(expected);
done();
}, function(error) {
expect().toBeDefined();
done();
});
}
}, function(error) {
expect().toBeDefined();
done();
});
})
})(key, cases_expectations[testing_case][key]);
}
}
Two questions:
How can I or can I combine pending() with done()?
Is there a way to run describes within a promise?
Related
Note, related How to test if jQuery 3.0 beta is Promises/A+ compatible in browser?
For example, at promises-tests one of the tests is found at promises-tests/lib/tests/2.1.2.js
"use strict";
var assert = require("assert");
var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
var adapter = global.adapter;
var deferred = adapter.deferred;
var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.", function () {
testFulfilled(dummy, function (promise, done) {
var onFulfilledCalled = false;
promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
setTimeout(done, 100);
});
specify("trying to fulfill then immediately reject", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
d.resolve(dummy);
d.reject(dummy);
setTimeout(done, 100);
});
specify("trying to fulfill then reject, delayed", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
setTimeout(function () {
d.resolve(dummy);
d.reject(dummy);
}, 50);
setTimeout(done, 100);
});
specify("trying to fulfill immediately then reject delayed", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
d.resolve(dummy);
setTimeout(function () {
d.reject(dummy);
}, 50);
setTimeout(done, 100);
});
});
Requirement:
The ability to run the test in the browser without reliance on node.js , a server, or installing libraries?
Question:
How can this test be converted to a version using native methods available at browsers, for example Console methods; that is, substitution of console.assert() or other native method available at window for describe() and specify()?
I still think this is a terrible idea, but here's a (trivial) example:
var assert = {};
assert.equals = function(expected, value, msg) {
var message = msg || "Test ";
try {
expected == value;
console.log(message, "passed");
catch (e) {
console.log(message, "failed", e);
}
};
Now repeat for >, <, ===, ranges, exceptions when they're expected, specific exceptions (e.g. TypeError, SyntaxError), typeof, instanceof, other types (built-ins, constructed), problematic values like NaN and null, etc.
NOTE: console.assert
There are some problems with console.assert. First, its non-standard and may not behave identically across platforms. Second, there is, AFAIK, no good way to abstract it: you'd end up doing just as much work as the above solution using console.log unless you use eval and string arguments:
function assert(str, msg) {
try {
console.assert(eval(str), msg);
catch (e) {
console.log(msg, " failed:", e);
}
}
assert("3 === 4", "Three equals four"); // logs the assertion failure.
Needless to say, I do not recommend this, as manually constructing the strings is error-prone, eval (even though safe in this case) is a notorious perf killer, and not using eval means using a parser and bam we're right back to the library bit.
Seriously, as you go down this road you will think of more and more things you want to be in there (see my list above) and you will realize you are writing a library when you could have used one.
UPDATE
Per your question in the comments below, many testing libraries (e.g. mocha, jasmine) use a format like this:
wrapper('name of the thing being tested', function(done) {
innerWrapper('description of the test', function(done) {
assert(someAssertion);
});
innerWrapper('some other test for that function', function(done) {
assert(somethingElse);
someAsyncFunction().then(function(value) {
assert(somethingAboutValue);
done();
});
});
});
The 'wrapper', 'innerWrapper', and 'assert' are added by including the testing library in the code or running the command (for cli) i.e. 'mocha tests.js' instead of 'node tests.js'. The setup may or may not use the inner functions to specify subtests. 'Done' is an argument to the callback that can be used to signal the end of an async test.
QUnit is a little simpler in its API, but not too far off.
UPDATE 2
Those for the most part are names for the same things: functions that wrap a test condition to make sure that correct messages are logged, or exceptions caught, or async gets a chance to finish. The assert tests the actual condition to be evaluated. The adapter reference means wrapping the jQuery deferred constructor to match the API in the spec.
I'm writing a background job function on Parse.com CloudCode. The job needs to call the same function (that includes a Parse.Query.each()call) several times with different parameters, and I want to chain these calls with promises. Here's what I have so far:
Parse.Cloud.job("threadAutoReminders", function(request, response) {
processThreads(parameters1).then(function() {
return processThreads(parameters2);
}).then(function() {
return processThreads(parameters3);
}).then(function() {
return processThreads(parameters4);
}).then(function() {
response.success("Success");
}, function(error) {
response.error(JSON.stringify(error));
});
});
Below is the processThreads() function:
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
// do something
});
}
My questions are:
Am I chaining function calls using promises correctly?
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged..
Am I chaining function calls using promises correctly?
Yes.
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged.
I think I'm right in saying that, if "do something" is synchronous, then zero "Hello" messages can only happen if :
an uncaught error occurs in "do something" before a would-be "Hello" is logged, or
every stage gives no results (suspect your data, your query or your expectation).
You can immunise yourself against uncaught errors by catching them. As Parse promises are not throw-safe, you need to catch them manually :
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
try {
doSomething(); // synchronous
} catch(e) {
//do nothing
}
});
}
That should ensure that the iteration continues and that a fulfilled promise is returned.
The following example shows as use promises inside your function using a web browser implementation.
function processThreads(parameters) {
var promise = new Promise();
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
try {
threadQuery.each(function(thread) {
console.log("Hello");
if (condition) {
throw "Something was wrong with the thread with id " + thread.id;
}
});
} catch (e) {
promise.reject(e);
return promise;
}
promise.resolve();
return promise;
}
Implementations of promise:
Web Browser https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
jQuery https://api.jquery.com/promise/
Angular https://docs.angularjs.org/api/ng/service/$q
I want to test a function returning a promise.
In this particular test, the promise is expected to be rejected with an Error object containing the classical message field (in this test, it is expected to equal "my error message") and a custom field I added named code, which is a string (like "EACCESS", "ERIGHT", etc, in this test it is expected to equal "EFOO")
I want to use chai-as-promised for that.
return expect(foo()).to.eventually.be.rejectedWith("my error message");
This assertion is working but now I would like to test the code field too.
How to do that?
If you're using Chai-As-Promised (as you say you are), then it allows for chaining off of rejectedWith - and it sets the chain assertion object to be the error object - meaning anything after rejectedWith() is now going to assert on the Error. This lets you do cool things like:
return expect(foo()).to.eventually
.be.rejectedWith("my error message")
.and.be.an.instanceOf(Error)
.and.have.property('code', 'EFOO');
Some of the chai methods also chain, so you can use that to make some quite deeply nested assertions about the error:
return expect(foo()).to.eventually
.be.rejectedWith("my error message")
.and.have.property('stack')
.that.includes('myfile.js:30')
Having version 5.1.0 of ChaiAsPromised, solution from Keithamus did not work for me - rejectedWith did not gave me the error object to assert, but "rejected" did:
return expect(foo())
.to.be.rejected
.and.be.an.instanceOf(Error)
.and.have.property('code', 'EFOO');
For asserting multiple properties
return expect(foo())
.to.be.rejected
.then(function(error) {
expect(error).to.have.property('name', 'my error message');
expect(error).to.have.property('code', 'EFOO');
});
#Markko Paas's solution didn't work for me until I added 'eventually', or else rejected value is always {} empty object.
return expect(foo())
.to.eventually.be.rejected
.and.be.an.instanceOf(Error)
.and.have.property('code', 'EFOO');
You can perform complex tests on errors using rejected.then:
it('throws a complex error', function () {
return expect(foo()).to.eventually.be.rejected.then((error) => {
expect(error.code).to.equal('expected code');
// other tests
// alternatively,
expect (error).to.eql({
foo: 'foo',
bar: 'bar
});
});
});
In my case, since I was using chai-as-promised in an async function, all I had to do is add an await statement before expect(promise).to.be.rejectedWith(errorMessage), e.g:
it('should reject', async () => {
await expect(promise).to.be.rejectedWith(errorMessage);
// ^^^^^
});
Chai-As-Promised did not work for me, because it does not throw if you expect something to be rejected and it does not reject.
Then I used the following, which IMO is also quite expressive:
//...
await $radioButton.click();
const executed = await(async () => {
try {
await tools.waitUntil(() => {
return consoleMessages.length === 2;
}, 1000); // 1000 is the timeout in milliseconds. waitUntil() rejects if it does timeout.
return true;
} catch (error) {
return false;
}
})();
chai.assert.strictEqual(executed, false);
I have a function in JavaScript that uses the q library:
validateOnSelection : function(model) {
this.context.service.doLofig(model).then(function(bResult) {
if (bResult) {
return true;
} else {
throw new Error(that.context.i18n.getText("i18n", "error"));
}
});
}
How can I check in qunit that the result is error? Let's assume that the result: bResult is false and Error should raise.
I tried:
test("Basic test ", {
// get the oTemplate and model
return oTemplate.validateOnSelection(model).then(function(bResult) {
// Now I need to check the error
});
}));
The problem that I didn't get to the check "// Now I need to check the error"
There are lots of problems here. For one, you don't have any way to let the calling code know that your function has finished. Without that, QUnit can't determine when to run the assertions. Then you'll need to use QUnit's async ability, otherwise the test function finishes before your promise is resolved. Additionally, you can use the throws assertion to check for an error. The example below is using QUnit version 1.16.0 (the newest version).
validateOnSelection : function(model) {
// Instead, return a promise from this method which your calling code can use:
var deferred = Q.defer();
this.context.service.doLofig(model).then(function(bResult) {
if (bResult) {
// return true; this doesn't really do anything, it doesn't return anywhere.
// instead, resolve the promise:
deferred.resolve(true);
} else {
// we don't really want to "throw" here, we nee to reject the promise:
// throw new Error(that.context.i18n.getText("i18n", "error"));
deferred.reject(new Error(that.context.i18n.getText("i18n", "error")));
}
});
return deferred.promise;
}
Now we can set up our test to wait for the promise to finish and then test the result...
QUnit.test("Basic test", function(assert) {
// get the oTemplate and model
var done = QUnit.async(); // call this function when the promise is complete
// where does `model` come from???
oTemplate.validateOnSelection(model).then(function(bResult) {
// Now I need to check the error
assert.ok(bResult instanceof Error, "We should get an error in this case");
done(); // now we let QUnit know that async actions are complete.
});
});
Hi i am using async module of node.js for implementing a for loop asynchronously.
My question is: how to break the loop execution and get out of the loop? I tried giving return , return false but no luck.
Here is the sample code:
async.until(
function() { return goal; },
function(callback) {
async.each(_rules,
function(rule,callback) {
var outcome = true;
.... some code ....
if(changes){
console.log("hi");
return false;// HERE I NEED TO BREAK
}
else
callback();
},
function(err){ }
);
if (!changes || session.process)
goal = true;
callback();
},
function(err){ callback(session); }
);
async.until repeatedly calls function until the test returns true. So test must return true so that you exit the loop. This is opposite of async.whilst which runs repeatedly while test evaluates to be true.
async.each calls the functions in parallel so what it returns does not matter. It is not a loop which you can break, but an iterator looping over the array. Your condition to stop using async.each should be in test for async.until and you should iterate over the rules yourself.
There isn't really a "loop" as such to break out of. All your items in your collection are used in parallel
The only way to "break" the "loop" is to call the callback with an error argument. As there is nothing to stop you from putting other things in there you could hack it a little bit.
From the docs:
Note, that since this function applies the iterator to each item in
parallel there is no guarantee that the iterator functions will
complete in order.
Even if you return an error, you will still have several outstanding requests potentially so you really want to limit the amount of items you use in one go. To limit the amount of outstanding requests, you could use eachSeries or eachLimit.
For example:
async.each(_rules,
function(rule,callback) {
if(changes){
return callback({ data: 'hi'}); // stop
}
...
if(realerror){
return callback({ error: realerror}); // stop with an error
}
callback(); // continue
},
function(result){
if(!result){
return othercallback('no results');
}
// check if we have a real error:
if(result.error){
return othercallback(result.error);
}
return othercallback(null, result.data);
}
);
PS: if you're not doing async, use underscore
You have also async.detect
Returns the first value in coll that passes an async truth test. The iteratee is applied in parallel, meaning the first iteratee to return true will fire the detect callback with that result.
// asynchronous function that checks if a file exists
function fileExists(file, callback) {
fs.access(file, fs.constants.F_OK, (err) => {
callback(null, !err);
});
}
async.detect(['file3.txt','file2.txt','dir1/file1.txt'], fileExists,
function(err, result) {
console.log(result);
// dir1/file1.txt
// result now equals the first file in the list that exists
}
);