THE SITUATION:
Hello guys. I am learning jasmine to test my angular app.
I have create a basic function that does multiply two numbers.
If the parameters given are not a number, the function throw an error.
I then made two very basic tests.
The first to check if the function properly multiply the numbers.
The second to check if the function properly throw an error if a string is given as parameter.
The first test pass, the second not. And i don't understand why.
THE CODE:
The function:
function Multiply( num1, num2 )
{
var result;
if (isNaN(num1) || isNaN(num2))
{
throw new Error("not a number");
}
else
{
result = num1 * num2;
return result;
}
}
The spec:
describe('The function', function ()
{
it('properly multiply two numbers', function ()
{
result = Multiply(10, 5);
expect(result).toEqual(50);
});
it('throw an error if a parameter is not a number', function ()
{
result = Multiply(10, 'aaaa');
expect(result).toThrow(new Error("not a number"));
});
});
THE OUTPUT:
2 specs, 1 failure
Spec List | Failures
The function throw an error if a parameter is not a number
Error: not a number
Error: not a number
at Multiply (http://localhost/jasmine_test/src/app.js:8:9)
If i understand properly Jasmine. Both test should pass, because in the second case the function throw the error as we expected.
THE QUESTION:
How can i test if a function properly throw an error?
EDIT:
I am trying this new code, but is still not working:
describe('The function', function ()
{
it('throw an error if a parameter is not a number', function ()
{
expect(function() { Multiply(10, 'aaaa') }).toThrowError(new Error("not a number"));
});
});
OUTPUT:
2 specs, 1 failure
Spec List | Failures
The function throw an error if a parameter is not a number
Error: Expected is not an Error, string, or RegExp.
If I understand correctly you need to pass a function into the expect(...) call.
The code you have here:
expect(result).toThrow(new Error("not a number"));
Is checking the result of Multiply, which when it works is fine, but like I said .toThrow() expects a function, I'd use an anonymous function instead, see below:
expect( function(){ Multiply(10, 'aaaa'); } ).toThrow(new Error("not a number"));
EDIT: Did a quick search and this blog post is a very detailed explanation of what I am trying to say.
You need to put the code you expect to throw an error into a function:
expect(function () {
Multiply(10, 'aaaa');
}).toThrow(Error, 'not a number');
Otherwise, when you run your assertions, the error has already been thrown outside the scope. You can see available syntax for error matching in jasmine docs
The above methods are exactly true for exceptions handled inside the methods.
When you've to test the services you can use mocking mechanism to do that.
Use the advantage of NgModule providers section to create the mocks.
In describe() block,
providers: [{ provide: APIService, useValue: { api: { filterTaskDetails: () => throwError('Error') }}}]
throwError to be imported from rxjs.
In expect() block test it as,
spyOn(component['toaster'], 'showError');
/** add respected it() blocks**/
expect(component['toaster'].showError).toHaveBeenCalledTimes(1);
Related
I am new to unit testing using mocha and sinon , just to learn more I have stubbed a function to return some string, in my code I call this function as inner function. My test is to see if the stubbed return string is assigned to a variable. Please look at the code snippet to understand more
file.specjs
let sinon = require("sinon");
let filejs = require('./file.js');
let expect = require ("chai").expect;
it('should run only the outer function' ,function() {
// I try to stub my function here
sinon.stub(filejs,'test1').callsFake ((someArg) => {
return "stubbed string";
});
// Now I will call my test outer function
filejs.test();
expect(filejs.param).to.equal("stubbed string");
})
let param;
module.exports = {
test,
test1
}
function test () {
module.exports.param = test1();
}
function test1() {
console.log("should not be called);
let data = "some data";
return data;
}
As I have already stubbed the function test1, I don't expect this to be called, and return from test1 is assigned to param and since we have faked the function to return a different string, I expect this string to be set to param variable.
But when I run the test I see this error
AssertionError: expected 'some data' to equal 'stubbed string'
Try the following edit...
function test () {
module.exports.param = module.exports.test1();
}
For what you are trying to do to have a chance at working. You need sinon to modify the module.exports and the code under test needs to read test1() from that object. It may need to be nested deeper in order to modify it... I don't know. I had troubles using sinon.stub(require('./something'))
I think I got it working in a repl.it
https://repl.it/repls/NegativeEnragedMainframe
I have a function which throws some object in certain cases. I wrote a jasmine expect matcher with toThrow but its not working. Not sure why its failing. Any help will be appreciated.
fit("The 'toThrow' matcher is for some object", function() {
function baz(x) { // this is the function to test
if(x === 1) {
return 1;
} else {
throw {status: 515};
}
};
expect(baz(1)).toBe(1); // matched perfect.
expect(baz(2)).toThrow({status: 515}); // failing with message Error: "[object Object] thrown"
});
How to write matcher for function call baz(2)??
According to the documentation, you have to give the reference to the function to expect, not the return value of the function.
see https://jasmine.github.io/api/3.5/matchers.html#toThrow
Example
function error() {
throw 'ERROR';
}
expect(error).toThrow('ERROR')
For your case, you can wrap your function call into another function. You can directly inline the declaration of that function inside the expect argument:
expect(() => baz(2)).toThrow({status: 515});
// equivalent with
expect(function(){ baz(2) }).toThrow({status: 515});
Another way is to use .bind to attach parameters to the function without calling it.
expect(baz.bind(null, 2)).toThrow({status: 515});
// ^^^^ ^
// context first parameter
So, i am trying to create a custom function that will allow me to check if a field contains a number or a text, but for further test i will need to check more complex stuff, like if the sum of some table equals something, etc.
I can't find examples of custom functions for example:
function isNumber(n) {
let a = parseInt(n);
if (a > 0 || a < 0) {
return true
}
return false
}
test('Test example', async t => {
await t
.expect(isNumber(Selector('#thisNum').innerText)).ok('This is a number' );
});
The assertion message will only be displayed when the assertion fails (refer to the message parameter). For example,
await t
.expect(failingValue).ok('failingValue is not a number');
Would display something like the following on a failed test:
1) AssertionError: failingValue is not a number: expected false to be truthy
Therefore, I'd never expect to see the "This is a number" message displayed.
As for the function, I've experienced a couple of instances where the promise wasn't resolved yet, so try awaiting the number selector:
await t
.expect(isNumber(await Selector('#thisNum').innerText)).ok('This is a number');
Hope this helps.
We have the following working test example:
"use strict";
var should = require("chai").should();
var multiply = function(x, y) {
if (typeof x !== "number" || typeof y !== "number") {
throw new Error("x or y is not a number.");
}
else return x * y;
};
describe("Multiply", function() {
it("should multiply properly when passed numbers", function() {
multiply(2, 4).should.equal(8);
});
it("should throw when not passed numbers", function() {
(function() {
multiply(2, "4");
}).should.throw(Error);
});
});
There's no explanation on why the second test needs to be run with the hack
(function() {
multiply(2, "4");
}).should.throw(Error);
If you run it like
it("should throw when not passed numbers", function() {
multiply(2, "4").should.throw(Error);
});
the test fails
Multiply
✓ should multiply properly when passed numbers
1) should throw when not passed numbers
But running the function as a regular node script does fail:
Error: x or y is not a number.
at multiply (/path/test/test.js:7:11)
So I don't get why the should doesn't pick up the fact that it throws error.
What is the cause of needing to wrap this in anonymous function() { } call? Is it do to tests running asynchronously, or scope or something?
Chai is regular JavaScript, not magic. If you have an expression a().b.c() and a throws, c() can’t catch it. c can’t even run. The engine can’t even know what c is, because a didn’t return a value whose .b.c could be looked up; it threw an error instead. When you use a function, you have an object on which to look up .should, which in turn gives you an object on which to look up and call .throw.
That’s why it can’t do that, but from an API point of view, there’s nothing wrong: .should.throw is just an assertion on a function instead of a function call.
I’d also recommend using Chai’s expect, which doesn’t insert itself into Object.prototype to give the appearance of magic.
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);