If I have a function that throws an error and I want to test for that error, I'd write something like this:
test('throws at something that is not a string', t => {
t.plan(1)
t.err(loadString(9))
})
But this always results in an actual error coming from the function while executing:
And there's the not ok 1 no plan found and not ok 2 no assertions found, which is also weird. How can I make sure it doesn't actually throw?
If you want to test if the function will throw, you have to use t.throws and pass it a function (not the error value). Here, I assume loadString is the actual function you are testing, so YOU are actually causing it to throw instead of Tape invoking it and catching the error.
Try this instead:
t.throws(function() { loadString(9); });
Related
Problem summary:
I wrote a function called getImagePathsFromCatalog that takes a string representing the location of a catalog (XML) file. It reads product info from the catalog file and produces an array "map"(*) consisting of productIds (key), each with an array of their associated images (value).
In my application, this runs fine and always returns the expected output, which should look something like this (formatted for readability):
[
'TEST-123': [ 'products/TEST-123.jpg' ],
'TEST-12345': [ 'products/Equipment/TEST-12345.jpg',
'products/Equipment/TEST-12345_1.jpg' ]
]
Background Info:
I have tried using various syntax to construct the it block (using async/await, using .then); using return and expect; using various Chai methods (be, should, assert, etc.); assigning the resolve value of the promise to a let for use in the assertion; using done; and some other things I'm sure I'm forgetting.
I have also tried different expectations, including to.be.array, to.be.ok, to.exist, and etc., but all end in the same error.
I have installed and am importing all of the required libraries and functions: chai, chai-as-promised, chai-arrays, chai-asserttype, chai.expect (etc.) and am also "using" them, as in chai.use(chaiAsPromised), etc.
(*) Note: Due to project specs, I have to use an array that behaves more or less like a map, rather than using an actual map object. I know this may cause issues with tests like to.be.array, but it shouldn't prevent me from expecting things like the result existing or coming back non-null (at least I wouldn't think it would?.)
Code:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('../imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});
Expected and actual result summary:
Expected result: Some tests (even just basic assertions about the existence or non-null/non-undefined status of the result) should pass.
Actual result: All tests written for this function return the same error ("cannot read property 'length' of undefined").
I've been researching this error and haven't found a solution that works for me yet. Thank you in advance for any assistance.
Looking at the chai expect documentation, it looks like array expectations are managed through the following syntax:
expect(foo).to.be.an('array');
Can you try this? You may need to update to the latest chai version.
Very unexpected, but the issue was actually the file path I was passing-into the function as an argument. The path was referencing the file from the test's location in the file system, but when my coworker suggested doing a console.log(process.cwd()); in the test just to double-check (after trying A LOT of other things that seemed more obvious), we found that the file was behaving as if it was in the root instead of in its subfolder. After switching the path argument to start at the root level rather than the subfolder the test file resides in, the tests started working perfectly. I still don't understand why this is the case, but maybe this will help someone else in the future who is banging their head against a similar Mocha/Chai mystery!
Doesn't work:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('../imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});
Does work:
describe('catalogImages.getImagePathsFromCatalog(catalogFile)', function () {
it('function should return an array', function() {
catalogImages.getImagePathsFromCatalog('./imports/catalog.xml')
.then(catalogImagePathsMap => {
return expect(catalogImagePathsMap).to.be.array();
});
});
});
I'm using constructor functions with prototypical inheritance to build a small library. I'm trying to test that one of the methods throws an error.
Sample code:
function Thing() {
this.x = 'ok'
}
Thing.prototype.trySomething = function() {
throw new Error('test error')
}
const thing = new Thing()
tap.throws(function() { throw new Error('before')}) // this is fine
tap.throws(thing.trySomething()) // urgh
tap.throws(function() { throw new Error('after')}) // never runs
Looks like tap is just throwing the error from trySomething. Running the tests gives me the following results:
ok 1 - expected to throw
not ok 2 - test error
And test 3 doesn't run.
I'm using node-tap (https://github.com/tapjs/node-tap).
t.throws(fn, [expectedError], message, extra)
Expect the function to throw an error. If an expected error is provided, then also verify that the thrown error matches the expected error.
but the same thing seems to happen with native assert.
Any idea what is going on here and how to write a passing test?
The tap.throws assert function accepts a fn/function
Just do this
tap.throws(thing.trySomething, {})
remove the (), it will work fine
I'm struggling to find a way to unit test the validation of a field in mongoose. I currently have the following test:
it('should be invalid if googleId is empty', () =>
testBook.googleId = '';
const newBook = new Book(testBook);
return newBook.validate().catch((err) => {
should.exist(err.errors.googleId);
});
});
This test is not working as intended because, if I have a correct validation for googleId, the test will pass because it will assert the err.errors.googleId exists, but if I don't have any validation for googleId, it will also pass because the .catch() never gets called and it assumes the test is passing.
I've tried putting should.fail() inside a .then(), but that way it always ends up in .catch() but with different errors, if there's no validation it will be catched with the should.fail() error, if there's validation it will catch with the model validation error.
What would be the best way to achieve this kind of testing?
Thanks alot!
EDIT:
I've tried using the callback of newBook.validate() and it works, but I never use callbacks for model methods, I always tend to use promises, so should I be using the callback in this particular case or there's still a better way to handle it using promises?
You can use the second parameter to then and put something that throws in the first for example:
return newBook.validate().then(
resp => throw new Error("Test failed this should not resolve"),
err => should.exist(err.errors.googleId);
});
So now if the newBook.validate() is not rejected the test will fail. Since you aren't using catch() you don't stop it from causing the test to fail like it should. If the promise is rejected like it should be your test passes (assuming it meets the conditions set). This works because unlike catch() the error callback of then() doesn't catch errors thrown in then(). Of course you can put anything in the first callback that throws an error like assert.fail() or whatever your library has.
There is a chai-as-promised plugin that includes helpers to make Promise testing work more like normal chai expect/should does.
.rejectedWith and .rejected handle errors largely like regular .throw.
const chai = require('chai')
chai.should()
chai.use(require('chai-as-promised'))
it('should be invalid if googleId is empty', function(){
testBook.googleId = ''
const newBook = new Book(testBook)
return newBook.validate()
.should.be.rejectedWith('Validation Failed')
.and.eventually.have.nested.property('errors.googleId')
});
Just make sure you load the plugin last if you are loading any other chai plugins otherwise things go haywire.
I'm using Express and I'm writing a test for some part of my code using Chai.
At some point, the tested code is expecting the next() function from Express, so I'm mocking it by making a very simple function like follows:
var next = function(err) {
if (err) {
done(err)
} else {
done(
}
}
Meaning that if the mocked next() function receives an err, then it would fail the test by calling done(err), and if not, if will successfully finish the test by calling done().
Now, this seems to be working, but for the case when an err is received, I'm getting a truncated stacktrace in the test output. Suppose the error received is of prototype SomeError which in turn extends the default Error prototype, then in the console I see the following:
omeError
at Foo.bar.yeah (lib/foo/bar/yeah.js:78:12)
at Blah.someFunction (node_modules/yeah/lib/index.js:145:16)
Note the missing capital S in omeError when in fact it should be SomeError.
Btw, if I do a console.log(err) I get the following:
{ [SomeError]
foo: 'bar',
foo2: 'yeah'
name: 'SomeError' }
So, it seems the error object has a correct name SomeError. Altough I'm not sure why the string representation of the error also includes [SomeError] as a first line, in which seems a non-json syntax...
Why is this happening? I'm assuming something asyncronous failing to capure the complete stacktrace or something like that? Or could it be related to how SomeError extends Error. None of the above?
I have a function that takes an array, performs some operations and returns a number. Here's a truncated version:
function similarity(phrases){
if(phrases.length ==2){
//Lots of stuff
//concerning the phrases happen here
// and a variable called similarity is produced which
//contains a number
return similarity;
}else{
throw "Can only find similarity between 2 phrases";
}
}
If I call this function separately it works perfectly. If, however I try to use it inside setInterval, it throws an error.
setInterval(function(){console.log(similarity(["test","test"]))},2000);
The expected outcome is "1.00000" every 2 seconds. Instead, "1.00000" is printed once and then this error is thrown:
"setInterval(function(){console.log(similarity(["test","test"]))},2000);
TypeError:String is not a function"
(pointing to the "s" on similarity).
Any ideas on what the problem is?
Thanks a lot!
Looks like you're saving function result in variable called same way as function (similarity). This way you are overriding function from upper scope.
Try this:
function similarity(phrases) {
var similarity;
/* ... */
}