I'm writing a metalsmith plugin and its associated test suite with mocha.
The plugin should throw an exception if it lacks configuration:
function plugin(config) {
...
return function(files, metalsmith, done) {
...
done(new Error("config error"));
}
}
and I try to test it with mocha this way:
describe('my plugin', function() {
it('should throw an exception', function(done) {
var metalsmith = Metalsmith('test/fixtures/basic');
metalsmith
.use(myplugin({
someconfig: {
}))
.build(function(err,files) {
assert(err);
done();
});
});
});
When I run the test I have this result:
my plugin
✓ should throw an exception
1) should throw an exception
1 passing (31ms)
1 failing
1) my plugin should throw an exception:
Error: done() called multiple times
So it seems the test is ok but somehow is run another time, failing this time...
The problem was that the error was throwed inside a foreach loop, causing done() to be called multiple times:
Object.keys(files).forEach(function (file) {
...
done(new Error("config error"));
...
}
Adding a simple return does not work because you can't return from a foreach loop.
So using a simple for loop instead of the foreach, returning on the first error:
for (var file in files) {
...
return done(new Error("config error"));
...
}
Related
I have typescript class with some functions in it. Each function has a try catch block where upon hitting the catch it returns a pre defined response.
I am writing unit tests using mocha and chai and I am having trouble trying to explicitly hit the catch blocks.
For example, consider this simple function below
public async verifyCode(email: string, code: string) {
try {
let result: CodeInterface | null = //call db to get matching code
if(result === null) {
return {status: 401, message: INCORRECT_FELLOWSHIP_CODE_MESSAGE};
}
return result._id;
} catch (error) {
Logger.log(LoggerLogTypes.ERROR, {
class_name: "LaunchServiceImpl",
function_name: "verifyFellowshipCode",
message: error.message,
stack: error.stack,
additional_info: {
code
}
});
return false;
}
}
I want to write a test case where I can just send the control directly to the catch block to get the false value. This is a very simplistic example but in few other functions I am doing a lot more in the catch block.
My mocha unit test looks like this:
it("should go to the catch block and return false in case of exception", async function() {
let serviceInstance = new MyClass();
let response = await serviceInstance.verifyCode("john#test.com", "abc123");
// how do I directly jump to the catch block here??
expect(response).to.equal(false);
});
Suppose you have a function that will throw an error with the message User not found so you can test like this:
profileServiceInstance.getProfile('1').catch((err) => {
expect(() => {
throw err;
}).to.throw(Error, 'User not found');
});
My component throw error (and should be)
function ProblemChild() {
throw new Error("Error thrown from problem child");
return <div>Error</div>; // eslint-disable-line
}
My question is .. how can I test this??
const wrapper = mount(<ProblemChild />);
Something like this..
expect(wrapper).toThrow()
Wrap mounting within callback. Like so:
expect(() => mount(<ProblemChild />)).toThrow()
You can do something like this:
it('should throw an error when there is an error', () => {
try {
wrapper.setState({error: true});
expect(true).toBe(false); //Fail test if no error is thrown
} catch (error) {
expect(error).toBe(error); //Pass test if an error is thrown
}
});
In the example above i have the error in the state. It can be possible that your function is called onClick or onChange. In that case you have to use a simulate event before the assertion.
I try make some node script with co. It works well, but a have big delay before script finished. (I got "Ok" or "Bad" after one second, but script finishes 7 seconds after it). What I missed?
co(function *() {
let errors = yield someCheck(process.argv);
if (!errors) {
console.log('Ok');
} else {
console.log('Bad');
}
})(function(e) {
if (e) {
console.log(e);
}
});
I get a typeError when I run your code. I'm not sure what you are trying to do there, but I think you cannot pass the error handler as a second argument when calling co(), you have to use then() or catch() for error handling.
// Here's an example
co(function*() {
var result = yield Promise.resolve(true);
return result;
}).then(function(value) {
console.log(value);
}, function(err) {
console.error(err.stack);
});
// you can also catch the error
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
}).catch(onerror);
function onerror(err) {
// log any uncaught errors
// co will not throw any errors you do not handle!!!
// HANDLE ALL YOUR ERRORS!!!
console.error(err.stack);
}
I think process.exit() would fix your issue.
I've got a method that may throw an Error, but I'm having trouble writing a SinonJS/Mocha/Should unit test case for this condition.
Sample function under test:
function testError(value) {
if (!value) {
throw new Error('No value');
return false;
}
};
Sample test:
describe('#testError', function() {
it('throws an error', function() {
var spy = sinon.spy(testError);
testError(false);
spy.threw().should.be.true();
});
});
This outputs:
#testError
1) throws an error
0 passing (11ms)
1 failing
1) #testError throws an error:
Error: No value
at testError (tests/unit/js/test-error.js:6:14)
at Context.<anonymous> (tests/unit/js/test-error.js:14:6)
I was expecting Sinon to catch the Error and allow me to spy on the throw, but it seems to fail the test instead. Any ideas?
I referred to Don't sinon.js spys catch errors? but the only solution there is to use expect. I'd prefer to keep with a single assertion library if possible.
It appears that this works inside a try/catch:
function foo() { throw new Error("hey!"); }
var fooSpy = sinon.spy(foo);
try {
fooSpy();
} catch (e) {
// pass
}
assert(fooSpy.threw());
Note that you have to call fooSpy, not foo itself.
But also note that .should.be.true() is not part of Sinon, so you're probably already using Chai or a similar library, in which case the expect(foo).to.have.thrown() or assert.throws(foo, someError) syntax seems much nicer.
Update: If you're using ShouldJS, looks like you can use should.throws. I still think this is nicer than using the Sinon version for this purpose.
Revised
Following #nrabinowitz's helpful advice, here's a solution that uses should.throws. This avoids using Sinon.spy altogether.
describe('#testError', function() {
it('throws an error', function() {
should.throws(function() {
testError(false);
});
});
});
const bootstrap = async anyParam => {
if(!anyParam){
throw new Error('test')
}
await anyParam()
}
const spy = sinon.spy(bootstrap)
try {
await spy();
} catch (e) {
expect(e.message).to.be.equal('test')
spy.called = false
}
expect(spy.called).to.be.equal(false);
UPDATE 2-July-2013
Using the last approach with the wrapped anonymous function can introduce some strange testing behavior. If you expect a thrown error, but there is an error in the actual execution the test will pass, but will pass on any error and probably not the one you were expecting. I do two things, if its an error i am specifying i ensure the error text returned from the stub and the thrown error are the same. If it is from a third party library where i don't necessarily know the errors i will put some arbitrary value so that i know the passes are specific to the units i am testing. Example using sinon.js to stub some shizzle.
it('should return an error if the save fails', function(){
stubCall.restore();
stubCall = sinon.stub(Provider, 'save').callsArgWith(1, new Error('Save Error'));
(function(){
User.create(fakeUser, function(err, user){
if(err)
throw err;
});
}).should.throw("Save Error");
});
I am getting into Node.js and trying to do some Behaviour Driven Development(BDD) with Mocha and should.js (and sinon in there as well at some point).
One thing i am having problems with is testing errors returned by a call back, for instance this simple test:
it('should return an error if you try and execute an invalid Query object', function () {
var stubQuery = {};
Provider.execute(stubQuery, function (err) {
//should.not.exist(err);
should.have.property('message', 'Invalid Query Object');
});
});
with the function:
PostgresqlProvider.prototype.execute = function (query, cb) {
};
It doesn't matter what i try and test, the test always passes (should.exist, etc), and the only way i can get it to fail first is by adding cb(null); into the execute function, which kinda goes against what i am doing as i am trying to test before adding behaviour, not adding behaviour to fail a test.
I know i am doing some real newb mistake here, probably on a few levels but i am not grasping the testing of an error passed as a call back with is not thrown (which i am sure i could test easier)!
UPDATE
Using the code from Herman, i adjusted and that indeed words (the test is failing with no behavior defined)
it('should return an error if you try and execute an invalid Query object', function () {
var stubQuery = {};
Provider.execute(stubQuery, function (err) {
if (err) throw new Error(err);
}).should.throw();
});
issue now is i can't "catch" the error if sent in the callback to pass the test, the should.throw method doesn't get called, it just states that expected a throw to happen but didnt even though i am returning an error to the callback from my method. This could be down to scope
but i am unsure if i should add some form of closure, and at what level.
UPDATE 2
Figured it out, i needed to wrap the call to the function inside a closure, not the callback (Doh!) and then place the assert (should.throw) at the end of the closure;
it('should return an error if you try and execute an invalid Query object', function () {
var stubQuery = {};
(function(){
Provider.execute(stubQuery, function (err) {
if (err)
throw err;
});
}).should.throw();
});
You need to generate an exception inside your test function for the test to fail.
I'd add if (err) throw "Invalid query error " + err this way:
it('should return an error if you try and execute an invalid Query object', function () {
var stubQuery = {};
Provider.execute(stubQuery, function (err) {
if (err) throw "Invalid query error " + err
});
});
That should do.
(ON UPDATE)
should.throw() did not work for me either... I did this dirty hack to work the flow:
it('should return an error if you try and execute an invalid Query object', function () {
var stubQuery = {};
try {
Provider.execute(stubQuery, function (err) {
if (err) throw "Invalid query error " + err
});
// No error, keep on the flow
noError ();
} catch (e) {
// Error, let's continue the flow there
error (e)
}
function noError () {
// The flow in the event we have no error
// ... //
}
function error (errorObj) {
// The flow, in the event we have error
// ... //
}
});
Hope this helps!