How to intercept error when expect fails? Jasmine and frisby - javascript

I am creating HTTP tests with frisby.js which works on top of jasmine.js.
I also have to create some mongoDB objects to test against.
The problem is when I want to clean up these DB objects. When one of the expects fail I want to intercept that and call my own cleanup function. This means that after each failed test, I won't be able to remove the test objects from the DB.
The afterEach function in jasmine does not work properly and jasmine does not have any support for afterAll or beforeAll yet.
That is why I have made the tests as they are today.
it("testing userform get with correct userID and expect correct return", function() {
var innerUserId = userID;
frisby.create('Should retrieve correct userform and return 200 when using a valid userID')
.get(url.urlify('/api/userform', {id: innerUserId}))
.expectStatus(200)
.afterJSON(function(userform){
// If any of these fail, the after function wont run.
// I want to intercept the error so that I can make sure that the cleanUp function is called
// afterEach does not work. I have tried with done()
var useridJSON = userform.UserId.valueOf();
var firstnameJSON = userform.firstname.valueOf();
var surnameJSON = userform.surname.valueOf();
expect(firstnameJSON).toMatch(testUser.firstName);
expect(surnameJSON).toMatch(testUser.surname);
expect(useridJSON).toMatch(innerUserId);
})
.after(function(){
cleanUp(innerUserId);
})
.toss();
});
I am wondering if there is a way to intercept the error for "expect" in frisby or jasmine so that I can make a call to my own cleanup function before exiting.
Full example here

The quickest solution to this problem is to wrap the error code in a try-catch.
This is because if a javascript error occurs, jasmine will NOT keep running assertions. This is different from an assertion error. If an assertion error occurs, jasmine and frisby will keep on testing all the other assertions and then do the "after"-function.
.afterJSON(function(userform){
try {
var useridJSON = userform.UserId.valueOf();
var firstnameJSON = userform.firstname.valueOf();
var surnameJSON = userform.surname.valueOf();
catch(e) {
cleanUp(innerUserId);
// Can do a throw(e.message); here aswell
}
expect(firstnameJSON).toMatch(testUser.firstName);
expect(surnameJSON).toMatch(testUser.surname);
expect(useridJSON).toMatch(innerUserId);
})
This is not the pretty way, but works.
I ended up adding the throw(e) and placed the expects in a finally scope. This way I got jasmine to present all the errors that occured in the test.

As for "before exiting", how about this:
process.on('uncaughtException', function(err) {
console.error(' Caught exception: ' + err);
});

Related

The following error originated from your application code, not from Cypress

i tried to test this simple code
type Url = string
it('loads examples', () => {
const url: Url = 'https://www.ebay.com/'
cy.visit(url)
cy.get('input[type="text"]').type('book')
cy.get('#gh-btn').click();
})
then I faced this error
how can I solve it
Try adding this in support/index.js:
import './commands'
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from failing the test
return false
})
This should avoid the uncaught:exception in the click() method.
The accepted answer will cause Cypress to ignore all uncaught exceptions in the application. Generally, when these come up it means you found a bug in your app and should fix it.
Binding to the global Cypress object causes the event to stay bound for your entire test run. Usually, this isn't what you want.
If you actually need to ignore the exceptions though, you should be binding the event on the cy object so it's only persisted for the single test it's used in.
it('my test', () => {
cy.once('uncaught:exception', () => false);
// action that causes exception
cy.get('body').click();
});
I got Same Issue like this
Cypress Error
The following error originated from your application code,
not from Cypress. >
Cannot read properties of null (reading 'textContent')
When Cypress detects uncaught errors originating from
your application it will automatically fail the current test.
This behavior is configurable, and you can choose to turn this off by
listening to the uncaught:exception event.Learn more
No Need to worry about this.
Just paste this code to your index.js file :)
import './commands'
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})
Actually, if you click in the Learn more link that comes with the error, you'll get everything you need.
https://docs.cypress.io/guides/references/error-messages#Uncaught-exceptions-from-your-application
Quoting others:
won't this answer ignore all errors thrown by the application?
The accepted answer will cause Cypress to ignore all uncaught exceptions
That is true.
Also, #DJSDev did not work for me when using Cypress v10.0.3.
The aforementioned link provides a working alternative:
it('is doing something very important', (done) => {
// this event will automatically be unbound when this
// test ends because it's attached to 'cy'
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('something about the error')
// using mocha's async done callback to finish
// this test so we prove that an uncaught exception
// was thrown
done()
// return false to prevent the error from
// failing this test
return false
})
// assume this causes an error
cy.get('button').click()
})

Dealing with javascript errors in Jasmine tests

I'm developing a javascript application which listens to window.onerror() and reports to an endpoint me and my team are building. I'm this using grunt-contrib-jasmine for the unit test side of things and am running into issues since my test cases expect errors to be thrown.
When running test cases with phantom, this clutters up my output unnecessarily: http://cl.ly/image/1n3k3z2g2V2l
In the instance of the reference error, my test looks like this:
it("should report reference errors", function() {
spyOn(window.fred, 'sendRequest').andCallFake(function(details) {
expect(details.type).toEqual('js-error');
expect(details.message).toBeNonEmptyString();
});
runs(function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = 'reference_error;';
document.body.appendChild(script);
});
waits(2000);
runs(function() {
expect(window.fred.sendRequest).toHaveBeenCalled();
});
});
I want to be able to handle the ReferenceError which gets thrown at some point in the code. As best I can see, this happens indirectly at document.body.appendChild(script);. Jasmine's expect(fn).toThrow(e) looks like it would do the job if I used it here however the test still fails. I've also tried surround it with a try { ... } catch (err) { ... } but to no avail.
My question, does anyone have any suggestions on how to deal with this?
Normally jasmine will try to catch any exceptions that are thrown by your specs and have those mark the spec as failed. However, because this exception is being thrown by the browser when it tries to parse your script tag, jasmine isn't in the call stack. This is why toThrow doesn't work.
Also, as long as your onerror method returns true the browser should not do it's default error action, in this case log to the console.

Don't sinon.js spys catch errors?

So, I'm using mocha with chai to do my front-end testing, but I'm starting to incorporate sinon and really liking it. Except that testing throwing errors isn't working quite how the sinon docs seem to indicate.
Basically, I've got this method:
create: function(bitString, collectionType) {
var collection;
switch(collectionType) {
case 'minutesOfHour':
collection = this.createMinutesOfHour(bitString);
break;
case 'hoursOfDay':
collection = this.createHoursOfDay(bitString);
break;
case 'daysOfWeek':
collection = this.createDaysOfWeek(bitString);
break;
case 'daysOfMonth':
collection = this.createDaysOfMonth(bitString);
break;
case 'monthsOfYear':
collection = this.createMonthsOfYear(bitString);
break;
default:
throw new Error('unsupported collection type ' + collectionType);
}
return collection;
},
and I'm testing it with this expectation:
it('throws error if missing second arguement', function() {
sinon.spy(factory, 'create');
factory.create();
expect(factory.create).to.have.thrown();
factory.create.restore();
});
however, the error, which I'm try to test for, also seems to halt the execution of the test
I'd thought sinon.spy would include some try / catch logic internally, spy.throw doesn't seem as useful without it.
http://sinonjs.org/docs/#spies
Am I doing something wrong??
I think one thing you could try is asserting against a spy object instead of the method, assign it to a variable. Not really knowing how sinon deals with all this exception magic...I think it might just work as you had expected.
it('throws error if missing second argument', function() {
var spy = sinon.spy(factory, 'create');
factory.create();
expect(spy).to.have.thrown();
factory.create.restore();
});
If that still doesn't work I think you could also do this test with standard chai if need be, leaving sinon out of the equation and actually gaining the check that the error has the correct message.
it('throws error if missing second argument', function() {
expect(function() {
factory.create();
}).to.throw(/unsupported collection type/);
});
Or more concisely:
it('throws error if missing second argument', function() {
expect(factory.create).to.throw(/unsupported collection type/);
});
In your expectation, you mixed chai and sinon syntax. Try
expect(factory.create.threw()).to.be.ok();
Sometimes you'd like to check that errors were thrown on a function that you're not directly testing, i.e. the error method of an ajax call.
This approach worked swell for me:
errorStub = sinon.stub(jQuery, "ajax").yieldsTo("error");
try {
triggerError(); // triggers ajax call which yields to an error
}
expect(errorStub.threw()).to.be.true

Spying on console.error() with Jasmine

I'm actually new to JavaScript as well as Jasmine. So it might be something really obvious that fixes my problem but I can't see it.
I want to check if (an already existing) JavaScript application calls console.error() while loading. I don't really see a way how to realise this with Jasmine. I've included the JavaScript file as well as the spec file in the SpecRunner.html.
But I take it that I somehow need to "instantiate" the application in order to test if it throws any errors on the console, right?
Or should I include the SpecRunner.html code only for this purpose into the HTML code of the app?
You can spy on console.error like this:
beforeEach(function(){
spyOn(console, 'error');
})
it('should print error to console', function(){
yourApp.start();
expect(console.error).toHaveBeenCalled();
})
You can override the standard console.error function like this:
//call the error function before it is overriden
console.error( 'foo' );
//override the error function (the immediate call function pattern is used for data hiding)
console.error = (function () {
//save a reference to the original error function.
var originalConsole = console.error;
//this is the function that will be used instead of the error function
function myError () {
alert( 'Error is called. ' );
//the arguments array contains the arguments that was used when console.error() was called
originalConsole.apply( this, arguments );
}
//return the function which will be assigned to console.error
return myError;
})();
//now the alert will be shown in addition to the normal functionality of the error function
console.error( 'bar' );
This solution works with Jasmin or anything else. Just put the code above before the other codes and any call after this to console.error() will call the overridden function.
use toThow and toThrowError http://jasmine.github.io/edge/introduction#section-Spies:_and.throwError

Why there is no error thrown with Strophe.js?

Example code:
var connection = null;
function onConnect(status) {
im_a_big_error.log('wtf');
// Why it doesn't throw me an error here ??
}
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection.connect('admin#localhost', 'admin', onConnect);
});
It doesn't throw me an error in my Chrome console.
Do you have an idea to resolve this issue?
Yes, Strophe often catch errors by itself and currently doesn't provide any ability to get connection error information. While error catching is ok, the impossibility of catching errors by yourself is not very good. But you can fix it with the following code:
$().ready(function() {
connection = new Strophe.Connection('http://localhost:8080/http-bind');
connection._hitError = function (reqStatus) {
this.errors++;
Strophe.warn("request errored, status: " + reqStatus + ",
number of errors: " + this.errors);
if (this.errors > 4) this._onDisconnectTimeout();
myErrorHandler(reqStatus, this.errors);
};
connection.connect('admin#localhost', 'admin', onConnect);
});
where myErrorHandler is your custom connection error handler.
Yes, strophe swallows errors. Worse; After an error is thrown, the callback won't return true as it should, and strophe will remove the handler. As soon as an error occurs, the callback will never be called again.
I found the code from the current answer a bit hard to use. Internally, we use the following wrapper for every callback;
function callback(cb) {
// Callback wrapper with
// (1) proper error reporting (Strophe swallows errors)
// (2) always returns true to keep the handler installed
return function() {
try {
cb.apply(this, arguments);
} catch (e){
console.log('ERROR: ' + (e.stack ? e.stack : e));
}
// Return true to keep calling the callback.
return true;
};
}
This wrapper would be used as following in the code of the question;
connection.connect('admin#localhost', 'admin', callback(onConnect));
I've been playing with Strophe for a while now and I had to modify its default error handling routine to fit our needs
Strophe.js - log function - by default contains nothing - I added calls to my server side logging service for level === ERROR and level === FATAL
Strophe.js - run function - the default behavior for error is to remove the handler and to rethrow the error - since I already log the error server side I don't rethrow the error and decided to keep the handler (even if it failed). This behavior could make sense (or not) depending on your own implementation - since I use custom messages and have a rather complicated message processing routine I don't want the client to stop just because a message was not properly formatted when sent so I want to keep the handler, error or not. I replace the throw e line inside the run function with result = true;
Strope.js _hitError - as I mentioned, I don't want the client to ever disconnect so I rewrote the default behavior to never disconnect (no matter how high the error counter)
Hope these thoughts are of help to others - leave a comment if you have questions/want details.
I had a similar problem which I fixed using the approach given by tsds above. However with minimal modification. I created two connect methods one as connect and the other as connect_bak I placed the script
this.connection._hitError=function (reqStatus) {
client.connect_bak();
};
in my connectHandler function as well as the connect function. Such that the function is always binded on connect.

Categories

Resources