I need to test a directive, and it should throw an exception. How can I test that the exception was thrown, in jasmine?
The directives link function:
link: function() {
if(something) {
throw new TypeError('Error message');
}
}
I have not yet successfully implemented a test that actually catches the error and reports that the test was successful.
This is how I do it:
describe("myDirective", function() {
it("should throw an error", inject(function ($compile, $rootScope) {
function errorFunctionWrapper()
{
$compile(angular.element("<div my-directive></div>"))($rootScope);
}
expect(errorFunctionWrapper).toThrow();
}));
});
it("should throw an error", inject(function ($compile, $rootScope) {
expect(function () {
$compile(angular.element("<directive-name></directive-name>"))($rootScope.$new());
}).toThrow();
}));
EDIT: this seems to be fixed now. Tested with Angular 1.6.4.
In Angular 1.6, I'm been unable to catch errors thrown during the $compile phase. While not as elegant, I can still check against the $exceptionHandler.errors array (source):
it('throws an error', function() {
$compile(angular.element('<directive-name></directive-name>'))($rootScope.$new());
expect($exceptionHandler.errors.length).toBeGreaterThan(0);
});
Better yet, provide it with the exact error message(s):
expect($exceptionHandler.errors).toEqual(['first error', 'second error'])
exception handling in angular testing is better done with native angular service
https://docs.angularjs.org/api/ng/service/$exceptionHandler
this gives better handle on the thrown exceptions and provide a better native angular way to handle exception globally. say if at some point in time you can change the exception handling strategy of you application in a single place.
in testing this when used along with $exceptionHandlerProvider
https://docs.angularjs.org/api/ngMock/provider/$exceptionHandlerProvider
gives you a better handle on the generated exception and write specific tests.
for unit tests it is not a standard way in angular to validate an exception using .toThrow(); method of jasmine.
Related
I've found a lot of this sort of thing when refactoring our Jest test suites:
it('calls the API and throws an error', async () => {
expect.assertions(2);
try {
await login('email', 'password');
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});
I believe the expect.assertions(2) line is redundant here, and can safely be removed, because we already await the async call to login().
Am I correct, or have I misunderstood how expect.assertions works?
expect.assertions is important when testing the error scenarios of asynchronous code, and is not redundant.
If you remove expect.assertions from your example you can't be confident that login did in fact throw the error.
it('calls the API and throws an error', async () => {
try {
await login('email', 'password');
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});
Let's say someone changes the behavior of login to throw an error based on some other logic, or someone has affected the mock for this test which no longer causes login to throw. The assertions in the catch block won't run but the test will still pass.
Using expect.assertions at the start of the test ensures that if the assertions inside the catch don't run, we get a failure.
This is from Jest documentation:
Expect.assertions(number) verifies that a certain number of assertions
are called during a test. This is often useful when testing
asynchronous code, in order to make sure that assertions in a callback
actually got called.
So to put in other words, expect.assertions makes sure that the n number of assertions are made by the end of the test.
It's good to use it especially when writing a new tests, so one can easily check that correct assertions are made during the test. Async tests often pass because the intended assertions were not made before the test-runner (Jest,Mocha etc.) thought the test was finished.
I think we are missing the obvious here.
expect.assertions(3) is simply saying ...
I expected 3 expect statements to be called before the test times out. e.g.
expect(actual1).toEqual(expected1);
expect(actual2).toEqual(expected2);
expect(actual3).toEqual(expected3);
This timing out business is the reason to use expect.assertions. It would be silly to use it in a purely synchronous test. At least one of the expect statements would be found in a subscribe block (or other async block) within the spec file.
To ensure that the assertions in the catch block of an async/await test are adequately tested, expect.assertions(n) must be declared as shown in your code snippet. Such declaration is unnecessary for async/await tests without the catch block.
It seems quite unintuitive but it is simply the way it is. Perhaps, for certain reasons well deep within the javascript runtime, the test environment can detect when an await'ed' promise successfully resolved but cannot detect same for await'ed' promises that failed to resolve. The creators of the test environment would likely know verbatim why such is the case.
I have to admit that apart from error testing, I find it challenging to see a real use for expect.assertions. The above snippet can be changed to the following with the same guarantee but I think it reads more naturally and doesn't require me to count how many time I call expect. This is especially error-prone if a test if complex:
it('calls the API and throws an error', async () => {
try {
await login('email', 'password');
fail('must throw')
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});
I am new to NestJS and have written a basic unit test for my service/controller using an example from a couple of sites including the official NestJS website and even before I try to run this code,
it('should be defined', async () => {
const result:MyEntity[] = [{"test1":"value1"},{"test1":"value2"}];
jest.spyOn(service, 'findAll').mockImplementation(() => result);
expect(await controller.findAll()).toBe(result);
});
I see the following error.
Type 'MyEntity[]' is missing the following properties from type 'Promise<MyEntity[]>': then, catch, [Symbol.toStringTag], finallyts(2739)
index.d.ts(1163, 33): The expected type comes from the return type of this signature.
I am returning a Promise from my controller but somehow the compiler is expecting a try catch finally somewhere but I don't know where this should go.
If service.findAll normally returns a promise, you should make the mock return a promise as well. You can do this with jest.spyOn(service, 'findAll').mockResolvedValue(result). Now in your test you can do expect(controller.findAll()).resovles.toEqual(result) to make the method resolve properly and test the result.
I wrote some code using find method like below.
The adapter is RESTAdapter.
App.SessionManager = Em.Object.extend({
userIdChanged: function() {
App.User.find(this.get('userid')).then(
function(user) {
/* process something */
},
function(error) {
/* rejection handler */
/* I want to know what error returned by the server here.*/
/* but how? */
}
);
}.observes('userid'),
});
If my server returned some error(e.g. 401) the rejection handler called.
But it seems that the argument of handler doesn't have error information.
How can I know the error reason in rejection handler?
Handling of errors returned by the server is not yet fully implemented in ember data.
For reference, as stated in this blog post about ember-data:
We want to make error handling and dealing with client and server conflicts rock solid. A pull request from Paul Chavard is currently open and looks like a solid starting point for error handling. You should see much more development on this in the near future.
But there are some workaround you can do to get to that information in the meanwhile.
For example:
Ember.RSVP.configure('onerror', function(error) {
console.log(error.message);
console.log(error.stack);
});
Or use a dirty hack to get to that information. Since ember-data uses jQuery under the hood you can subscribe to ajaxError:
$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
// this will trigger anytime there is an error
});
Hope it helps.
you can also override this:
becameInvalid: function(record) {
// you can get errors just by doing record.errors, error keys must match
// the name of a field that you have defined in your model
}
and:
becameError: function(record) {
}
that's in case something happened in the server that caused an error. I just saw that you want to handle errors on promises, well, that's something different that I haven't dealt with. becameError might be fired.
UPDATE:
I just found something interesting that might help you: Ember-Data handling 401’s thanks to the new router, I haven't had the chance to use all those new features, but they look pretty cool.
How to get the errors returned when calling a RESTAdapter method (by example deleteRecord).
I can see this code in the sources but I do not clearly understand.
Is there a sample code available providing error handling management with ember-model RESTAdapter ?
settings.error = function(jqXHR, textStatus, errorThrown) {
Ember.run(null, reject, jqXHR);
};
The Ember Documentation describes promises and how to handle them.
I have a property that depends on another property. There is an error in my code that happens when the other property is present. The error is being swallowed by something, probably Ember. This makes debugging the error very hard. I have tried setting Ember.onerror to a function that just logs the error, it makes no difference.
Why is Ember swallowing this error, how can I make it not?
Code:
App.DashboardController = Ember.Controller.extend({
leaderboard: function() {
console.log("calling leaderboard");
var ces = this.get("engagements");
if (ces) {
console.log("before");
throw new Error("bad thing");
console.log("after");
}
console.log("done")
}.property("engagements")
})
Console log:
calling leaderboard
done
(setting engagements)
calling leaderboard
before
Version: Ember.js RC6, development
Turns out the property "engagements" was being set inside a promise fulfilment handler. This article explains how to catch errors that occur in those: http://blog.sensible.io/2013/06/10/promise-ate-my-homework-exception.html