I'm trying to write a test for the Jasmine Test Framework which expects an error. At the moment I'm using a Jasmine Node.js integration from GitHub.
In my Node.js module I have the following code:
throw new Error("Parsing is not possible");
Now I try to write a test which expects this error:
describe('my suite...', function() {
[..]
it('should not parse foo', function() {
[..]
expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));
});
});
I tried also Error() and some other variants and just can't figure out how to make it work.
Try using an anonymous function instead:
expect( function(){ parser.parse(raw); } ).toThrow(new Error("Parsing is not possible"));
you should be passing a function into the expect(...) call. Your incorrect code:
// incorrect:
expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));
is trying to actually call parser.parse(raw) in an attempt to pass the result into expect(...),
You are using:
expect(fn).toThrow(e)
But if you'll have a look on the function comment (expected is string):
294 /**
295 * Matcher that checks that the expected exception was thrown by the actual.
296 *
297 * #param {String} expected
298 */
299 jasmine.Matchers.prototype.toThrow = function(expected) {
I suppose you should probably write it like this (using lambda - anonymous function):
expect(function() { parser.parse(raw); } ).toThrow("Parsing is not possible");
This is confirmed in the following example:
expect(function () {throw new Error("Parsing is not possible")}).toThrow("Parsing is not possible");
Douglas Crockford strongly recommends this approach, instead of using "throw new Error()" (prototyping way):
throw {
name: "Error",
message: "Parsing is not possible"
}
As mentioned previously, a function needs to be passed to toThrow as it is the function you're describing in your test: "I expect this function to throw x"
expect(() => parser.parse(raw))
.toThrow(new Error('Parsing is not possible'));
If using Jasmine-Matchers you can also use one of the following when they suit the situation;
// I just want to know that an error was
// thrown and nothing more about it
expect(() => parser.parse(raw))
.toThrowAnyError();
or
// I just want to know that an error of
// a given type was thrown and nothing more
expect(() => parser.parse(raw))
.toThrowErrorOfType(TypeError);
A more elegant solution than creating an anonymous function whose sole purpose is to wrap another, is to use ES5's bind function. The bind function creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Instead of:
expect(function () { parser.parse(raw, config); } ).toThrow("Parsing is not possible");
Consider:
expect(parser.parse.bind(parser, raw, config)).toThrow("Parsing is not possible");
The bind syntax allows you to test functions with different this values, and in my opinion makes the test more readable. See also:
Does Jasmine's toThrow matcher require the argument to be wrapped in an anonymous function?
I replace Jasmine's toThrow matcher with the following, which lets you match on the exception's name property or its message property. For me this makes tests easier to write and less brittle, as I can do the following:
throw {
name: "NoActionProvided",
message: "Please specify an 'action' property when configuring the action map."
}
and then test with the following:
expect (function () {
.. do something
}).toThrow ("NoActionProvided");
This lets me tweak the exception message later without breaking tests, when the important thing is that it threw the expected type of exception.
This is the replacement for toThrow that allows this:
jasmine.Matchers.prototype.toThrow = function(expected) {
var result = false;
var exception;
if (typeof this.actual != 'function') {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected) || this.env.equals_(exception.name, expected));
}
var not = this.isNot ? "not " : "";
this.message = function() {
if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
return ["Expected function " + not + "to throw", expected ? expected.name || expected.message || expected : " an exception", ", but it threw", exception.name || exception.message || exception].join(' ');
} else {
return "Expected function to throw an exception.";
}
};
return result;
};
I know that is more code, but you can also do:
try
Do something
#fail Error("should send a Exception")
catch e
expect(e.name).toBe "BLA_ERROR"
expect(e.message).toBe 'Message'
In my case, the function throwing an error was async, so I followed this:
await expectAsync(asyncFunction()).toBeRejected();
await expectAsync(asyncFunction()).toBeRejectedWithError(...);
For CoffeeScript lovers:
expect( => someMethodCall(arg1, arg2)).toThrow()
For me, the posted solution didn't work and it kept on throwing this error:
Error: Expected function to throw an exception.
I later realised that the function which I was expecting to throw an error was an async function and was expecting the promise to be rejected and then throw an error and that's what I was doing in my code:
throw new Error('REQUEST ID NOT FOUND');
And that’s what I did in my test and it worked:
it('Test should throw error if request not found', willResolve(() => {
const promise = service.getRequestStatus('request-id');
return expectToReject(promise).then((err) => {
expect(err.message).toEqual('REQUEST NOT FOUND');
});
}));
it('it should fail', async () => {
expect.assertions(1);
try {
await testInstance.doSomething();
}
catch (ex) {
expect(ex).toBeInstanceOf(MyCustomError);
}
});
Related
I am trying to write a jest test for the xml2js parse.parseString, but when I pass in an invalid xml it just bypasses the parse.parseString function rather than return the error in the callback function. I want to trigger the error in the callback function.
import xml2js from 'xml2js';
const parser = new xml2js.Parser({
async: false
});
parser.parseString(`<_>&%xstest</_>`, (err, result) => {
if (err) {
console.log('error',err)
throw new Error(`Parser error: ${err}`);
}
console.log('result', result)
});
any idea on why I can not trigger the err in the callback of parse.parseString to call? I am unable to get parse.parseString to throw an error.
I am encountering an issue I do not understand.
Inside one service I have the following code.
when the code hits PATCH LINE, it jumps immediately to RETURN NOTHING LINE.
the catchError line is not hit.
the () line is not hit
the err line is not hit.
I have compared this to working services and I do not see any difference.
patchItem(item_: Project): Observable<Project> {
const url: string = `${this.serviceUrl}/${item_.id}`;
const data = JSON.stringify(item_);
console.log('inside patch item');
console.log(url);
this.http.patch(url, data, httpOptions) //PATCH LINE
.pipe(
catchError(err => this.handleError('PatchItem', err))
)
.subscribe((response) => {
console.log('item patched ');
return this.myResponse.push(response);
}
, err => console.log("inline error encountered")
,() => console.log("this seems to have completed")
);
console.log("return nothing"); //RETURN NOTHING LINE
return null;
}
The API is C# webapi 2
It is being hit.
There is a problem though, I am expecting the JSON to be passed in and webForm_ is always NULL.
This is probably an important clue.
Again, i compare this to working calls and can not find a difference.
When I inspect the variable in jquery, it has the correct value.
In postman, i get the expected response.
[System.Web.Http.HttpPatch]
[Route("{itemId_}")]
public IHttpActionResult PatchById([FromUri] int itemId_, [FromBody] Models.mpgProject webForm_)
{
return JSONStringResultExtension.JSONString(this, "foobar for now", HttpStatusCode.OK);
}
To save a cycle, here is the handleError function.
I have copy/pasted this from a service that is working as expected.
protected handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
How can Angular be skipping all the subscription stuff when it IS actually calling the API?
I think it has to be a simple syntax issue, but I have been staring at it for hours... :-(
Happy to post any other code requested...
tyia
ps - I would also be happy to retitle the question. I am at a loss for how to phrase a good question title for this...
Your handleError method returns a function (that returns an Observable) when it should return an Observable.
It looks as if the error handler you copied from another service does not fit here.
I could imagine an error handler like this:
private handleError<T>(operation = "operation", error: HttpErrorResponse) {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of({} as T);
}
This method returns an Observable of an empty object. However, this might not be the best option to react on a failing HTTP PATCH request. You would better throw that error up to the component and let the user retry.
When I have this code:
export default async ({
inputs,
stdin = process.stdin,
timeoutBetweenInputs = DEFAULT_TIMEOUT_BETWEEN_INPUTS
}: ISendInputsToCli): Promise<void> => {
// go through each input, waiting for the last timeout to
// resolve
// write the input to stdin
return inputs.reduce(
(previousPromise, input) =>
new Promise(async (resolve, reject) => {
await previousPromise;
let inputString;
if (typeof input !== "string") {
timeoutBetweenInputs = input.timeoutBeforeInput;
inputString = input.input;
} else {
inputString = input;
}
setTimeout(() => {
try {
stdin.write(inputString);
} catch (e) {
throw new Error(
`Unable to send input "${inputString}" to the cli. Please check to make sure your script didn't exit early.`
);
inputs.splice(0, inputs.length);
}
resolve();
}, timeoutBetweenInputs);
}),
Promise.resolve()
);
};
try {
await sendInputsToCLI({
inputs,
stdin: proc.stdin,
timeoutBetweenInputs
});
} catch (e) {
console.log("error here");
}
The application doesn't catch the error:
" to the cli. Please check to make sure your script didn't exit ea
rly.
52 | stdin.write(inputString);
53 | } catch (e) {
> 54 | throw new Error(
| ^
55 | `Unable to send input "${inputString}" to the cli. Please check to make sure your script didn't exit early.`
56 | );
57 | inputs.splice(0, inputs.length);
at Timeout._onTimeout (index.ts:54:19)
When you throw in the catch handler here:
setTimeout(() => {
try {
stdin.write(inputString);
} catch (e) {
throw new Error(
`Unable to send input "${inputString}" to the cli. Please check to make sure your script didn't exit early.`
);
inputs.splice(0, inputs.length);
}
the exception just goes back into the setTimeout() infrastructure. Because it calls its timeout callback from the event loop, not from your own code, there's no code of yours on the call stack at that time so you can't catch that exception.
Exceptions are difficult to propagate back through plain asynchronous callbacks. In fact, can't do it directly the way you've coded it. If this were my code, I would control my flow of asynchronous operations using only functions that return promises. When you have an asynchronous operation that uses a plain callback, not a promise, you "promisify" it with a small something like util.promisify() or a custom little promise wrapper and then you can throw to your hearts content and it will automatically propagate up to higher levels or back to the caller as a rejected promise.
FYI, it's also unclear why you have the line of code:
inputs.splice(0, inputs.length);
in that snippet since it can never be hit as it's after the throw.
For 2 days now, I have the issue that my lambda function using the ask-sdk-core v2.0.2 returns invalid responses.
A very simple setup:
HelloIntent is handled by HelloIntentHandler:
const HelloIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'HelloIntent';
},
handle(handlerInput) {
const speechText = 'Hello back';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
When I call this intent, the simulator goes straight to:
There was a problem with the requested skill's response
Using the ErrorHandler, I checked and the results of handlerInput.requestEnvelope.request.error are:
{ type: 'INVALID_RESPONSE',
message: 'An exception occurred while dispatching the request to the skill.' }
Question: What is the problem here? When I open the skill using the invocation name, the LaunchRequestHandler gets called and responds properly, but any other intent does not work.
Okay I found the problem, it is sort of difficult to deduct from the above error:
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HelloIntent';
},
In the canHandle function, you need to check for the request type and intent name.
I have something like:
doHTTPRequest()
.then(handleSuccess, checkNotFound)
.then(uploadSomething)
.fail()
.done()
Now when I'm getting into checkNotFound I need to check if I got 404, if so it's not an actual fail and I want to continue with uploadSomething, but Q chaining directs me to fail.
On any other failure (e.g: any other status code) I do want to get into fail().
The signature of then is promise.then(onFulfilled, onRejected) and it returns a new promise. The returned promises is resolved by whatever onFulfilled or onRejected returns or throws. That is to say, if you handle the error in onRejected and return a promise that fulfills, that will pass through to the result and skip your fail handler. In code:
function request(url) {
// returns a promise
}
function retryForeverLoop(url) {
return request(url).then(null, function (error) {
return retryForeverLoop(url);
});
// note that retrying forever might not work out well
}
retryForeverLoop(url)
.then(uploadSomething)
.done()
This could work if the library errbacks with the request object.
doHTTPRequest()
.fail(function(request){
if( request.isRequest && request.is404 ) {
//This will propagate to the next .then
return request;
}
else {
//This will propagate to the next .fail
throw request;
}
})
.then(handleSuccess)
.then(uploadSomething)
.fail()
.done()
Btw the second argument to .then is almost entirely about promise library interop. In user code you should use the usually available, far more readable .catch/.fail/.whatever alternative which also avoids the pitfall mentioned here https://github.com/kriskowal/q/#handling-errors. If such alternative is not available, even then .then(null, handler) should be used instead of passing error and success in a same .then call.
The sync equivalent of above code is like:
try {
try {
var a = doHTTPRequest();
}
catch(request) {
if( request.isRequest && request.is404 ) {
a = request;
}
else {
throw request;
}
}
uploadSomething(handleSuccess(a));
}
catch( e ) {
//This is the second .fail()
}