Don't sinon.js spys catch errors? - javascript

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

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()
})

How to intercept error when expect fails? Jasmine and frisby

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);
});

How to pass a custom error message to a jasmine matcher?

In all the testing frameworks I have used, there is an optional parameter to specify you own custom error message.
This can be very useful, and I can't find a way to do this out of the box with jasmine.
I've had 3 other developers ask me about this exact functionality, and when it comes to jasmine I don't know what to tell them.
Is it possible to specify your own custom error message on each assertion ?
Update 2022; Use .withContext(...) method instead of below (as optional parameter is deprecated).
Jasmine already supports optional parameter in all matchers (toBe, toContain, and others), so you can use:
expect(true).toBe(false, 'True should be false').
Then in output it will look like this:
Message:
Expected true to be false, 'True should be false'.
Link to commit (this is not described in documentation):
https://github.com/ronanamsterdam/DefinitelyTyped/commit/ff104ed7cc13a3eb2e89f46242c4dbdbbe66665e
If you take a look at the jasmine source code you will see that there is no way to set the message from outside a matcher. For example the toBeNaN matcher.
/**
* Matcher that compares the actual to NaN.
*/
jasmine.Matchers.prototype.toBeNaN = function() {
this.message = function() {
return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
};
return (this.actual !== this.actual);
};
As you can see the messages is hard coded into the matcher and will be set when you call the matcher. The only way I can think of to have your own messages is to write your matcher like described here
This issue is tracking interest in implementing custom error messages using a .because() mechanism.
In the meantime, avrelian has created a nice library which implements custom error messages using a since() mechanism – jasmine-custom-message.
Yes, it can be done.
You may define a custom matcher in global scope, overriding the error message in jasmine as below:
beforeEach(function () {
jasmine.addMatchers({
toReport: function () {
return {
compare: function (actual, expected, msg) {
var result = {pass: actual == expected};
result.message = msg;
return result;
}
}
}
});
});
Chain-call withContext() right after expect(). Example:
expect(myValue)
.withContext("This message will be printed when the expectation doesn't match")
.toEqual({foo: 'bar'});

How to assert that a function does not raise an exception

QUnit has an assertion for testing that a function raises an exception (QUnit/raises). Is it possible -- using QUnit -- to assert that a function does not raise an exception.
I realize that it is possible to test it like in the following code:
try {
theTest();
ok(true);
} catch (e) {
ok(false, "Expected to succeed");
}
But I think it ought to be possible using QUnit. Any clues?
There is no such method in qunit
However, if you just write the following code which is much shorter, you will obtain the same result with additionnal benefits
theTest();
ok(true, "My function does not crash");
1/ If the code of a test raises an exception, qunit will mark the test as failed.
2/ If you check the "no try/catch" checkbox, you will be able to debug where the exception was thrown, which is not the case with your try/catch
I had the same issue as you mentioned in the comment whereby my test which tests no Error is thrown would stop "badly" showing a badly formatted Died on test #1 message without any useful information.
I ended up using a mix of both; raises() for one test and try/catch for the other.
I used raises() for the test which tests that an Error is thrown, similar to this:
test("When myFunction() is called with a invalid instance Then Error is thrown", function () {
// Arrange
var testInstance = {};
// Act
raises(function() {
myFunction(testInstance);
}, Error, "myFunction() should throw an Error");
// Assert
// raises() does assertion
});
If the above throws an Error all is fine and if not a nice formatted message is displayed, similar to this:
myFunction() should throw Error
Result: No exception was thrown.
I then used try/catch for the tests which have to ensure no Error is thrown, similar to this:
test("When myFunction() is called with a valid instance Then no Error is thrown", function () {
// Arrange
var testInstance = new myObject();
var result;
// Act
try {
myFunction(testInstance);
result = true;
} catch(error) {
result = false;
}
// Assert
ok(result, "myFunction() should not throw an Error");
});
If the above throws no Error all is fine and if an Error is thrown a nice formatted message is displayed, similar to this:
myFunction() should not throw an Error
Source: ...

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