Unit testing mongoose validation with promise resolve/reject - node.js

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.

Related

How to mock a 'request' node module for testing in jest

I am new to writing test cases in jest and i wanted to test 'mark' function and want to mock 'request' node module. let's say this file's name is app.js and test file will be app.test.js
Can someone tell how to write its test case?
const request = require("request")
var test = {
mark: function(data,cb){
data.url = "localhost"
request(data, function(err,response,body){
if(!response){
err.response = false
}
cb(err,body)
})
}
}
module.exports = test;
If I understand your question correctly, you are asking two questions:
How to write the test and mock the request
What test cases you should write
Regarding the mock, I recommend using nock which is an npm module for mocking network requests.
To the second question, I believe that you should map the logic in the function and create the test cases from there. Think about the function, look at every if else/calculation/loop and it’s possible outcomes and create test cases from there.
The mark function doesn’t have a lot of logic, it’s send a request, updates the err variable according to the response and calling the callback.
The only outcome we can test to to see that if the request works, the cb is called correctly without modifications. And if the request returns empty, change the err var and call the callback.
To do so we need to mock the request and return a response to match the test case and validate that the cb was called correctly (can be done with a mock).
Test case example:
The test case may be a bit abstract since I don’t have the real use case of the function but it shows the point
it("Should mark response false when it does not exist", () => {
const DATA = {} // your data object
const callback = jest.fn();
nock('localhost')
.get('/example')
.reply(500) // Exaple of mocking the resonse. Custom it to be more specific depending on mark.
test.mark(DATA, callback);
// Verify that the function was called.
expect(callback).toHaveBeenCalled();
// Verify that `err.response` was false.
expect(callback.mock.calls[0][0].response).toBe(false)
})
You can read more about mocking and verifying parameters here.

control return automatically and last line not executing in unit test case using mocha and chai

I'm new in unit testing in node.js using mocha and chai.
I'm stuck at the problem in which control return automatically and not executing
chai.expect(123).to.be.a("string");
code is here
it.only("should fetch status",()=>{
return chai.request(server)
.get("/user/status")
.then((result)=>{
let data = result.body;
console.log("till here execute");
//this line is not executed and test case is passed even when the below line expect to fail the test
chai.expect(123).to.be.a("string");
})
.catch(err=>err);
});
Console show that above test case is passed I don't know how and why
chai.expect(123).to.be.a("string");
not executing
This is related to your catch.
Basically, when your chai.expect fails, it will throw an AssertionError.
Inside your given code, you are returning the catching error, and not throwing it.
According to chai.js official documents, found in https://www.chaijs.com/plugins/chai-http/, when dealing with promises, inside the catch you must throw the catch error.
In that way, change:
.catch(err=>err);
to:
.catch(err => {throw err});

How can I test that a promise have been waited for (and not just created) using Sinon?

Let's say I have a function:
const someAction = async(): Promise<string> => {
/* do stuff */
};
And I have some code that just needs to run this action, ignoring the result. But I have a bug - I don't want for action to complete:
someAction();
Which, instead, should've been looked like this:
await someAction();
Now, I can check that this action was ran:
const actionStub = sinon.stub(someAction);
expect(actionStub).to.have.been.calledWith();
But what's the most concise way to check that this promise have been waited on?
I understand how to implement this myself, but I suspect it must have already been implemented in sinon or sinon-chai, I just can't find anything.
I can certainly say that nothing like this exists in sinon or sinon-chai.
This is a difficulty inherent to testing any promise-based function where the result isn't used. If the result is used, you know the promise has to be resolved before proceeding with said result. If it is not, things get more complex and kind of outside of the scope of what sinon can do for you with a simple stub.
A naive approach is to stub the action with a fake that sets some variable (local to your test) to track the status. Like so:
let actionComplete = false;
const actionStub = sinon.stub(someAction).callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
actionComplete = true;
resolve();
});
});
});
expect(actionStub).to.have.been.calledWith();
expect(actionComplete).to.be.true;
Of course, the problem here is that awaiting any promise, not necessarily this particular one, will pass this test, since the variable will get set on the next step of the event loop, regardless of what caused you to wait for that next step.
For example, you could make this pass with something like this in your code under test:
someAction();
await new Promise((resolve) => {
setImmediate(() => resolve());
});
A more robust approach will be to create two separate tests. One where the promise resolves, and one where the promise rejects. You can test to make sure the rejection causes the containing function to reject with the same error, which would not be possible if that specific promise was not awaited.
const actionStub = sinon.stub(someAction).resolves();
// In one test
expect(actionStub).to.have.been.calledWith();
// In another test
const actionError = new Error('omg bad error');
actionStub.rejects(actionError);
// Assuming your test framework supports returning promises from tests.
return functionUnderTest()
.then(() => {
throw new Error('Promise should have rejected');
}, (err) => {
expect(err).to.equal(actionError);
});
Some assertion libraries and extensions (maybe chai-as-promised) may have a way of cleaning up that use of de-sugared promises there. I didn't want to assume too much about the tools you're using and just tried to make sure the idea gets across.

Mongoose: How do I avoid callback hell whilst allowing for stubbing of mongoose methods that do not return promises?

In my express node application written in typescript, I'm using mongoose and am trying to avoid callback hell whist allowing for stubbing the mongoose functions in my mocha/sinon unit tests.
Where mongoose does not return a promise (e.g. Model.count()) I'm wrapping the call in a new (awaited) promise which is resolved/rejected in the callback, like so:
const myModelCount = await new Promise((resolve, reject) => {
MyModel.count({}, (err, count) => {
if (err) {
reject(err);
return;
}
resolve(count);
});
});
This works great but I don't know how I can unit test the containing function and avoid a timeout error due to the unresolved promise. I want to stub MyModel.count so that it does not attempt to call the database, but how do I stub it so that the awaited promise is resolved and the program continues?
Doing something like:
sinon.stub(MyModel, 'count').returns(Promise.resolve(1));
does not work as it does not resolve the promise that is being awaited, and since I don't have access to the resolve/reject parameters I don't know how to resolve the promise in the stub.
Is there a way I can make the above work, or alternatively how I can refactor my code to both avoid callback hell and so that I can stub out the mongoose functions in unit tests?
Regarding returned promises, there seems to be a misunderstanding (easy to have with mongoose) with the model count method. It returns a query object if called without a callback and, as with all query objects, when invoked by exec() (also without a callback) will return a promise.
mongoose.Promise = global.Promise; // This sets the mongoose internal Promise to the native Promise, while it is not necessary it is highly advised.
const promise = MyModel.count().exec();
As far as testing, if the unit test is meant to test that a wrapper function is simply calling the wrapped function then I find this to be more of a functional test as any test should only be concerned with the inputs/outputs of a function (the API of the function) and not the inner workings (the implementation of the function). In other words the function should be a black box to any tests so that if the implementation were to change but the API remains constant then the tests should continue to pass.
That said if you really want to stub the model count/exec out then something like the following might work (pseudo coding here):
sinon.stub(MyModel, 'count').returns({
exec() { return Promise.resolve(1); }
});
Hope this clears up some possible confusion and helps!

How to make empty placeholder tests intentionally fail in Mocha?

I'm writing an API in NodeJS and testing using Mocha, Chai and SuperTest. I'm using a typical test-driven approach of writing the tests first then satisfying those tests with working code. However, because of the number of tests for all the different permutations, I've started writing empty placeholder tests so that I have all the it('should...') descriptions in place to remind me what to test when I get to that feature. For example:
it 'should not retrieve documents without an authorized user', (done) ->
done()
The problem with this is that done() is called without any assertion so the test is considered passing, so I've added the following assertion.
false.should.equal true # force failure
but it's a hack and the reason for failure that Mocha displays can seem confusing, especially when other full tests might be failing.
Is there any official way to intentionally fail placeholder tests like this in Mocha?
As of 05/19/2018 this is the official way: https://mochajs.org/#pending-tests
An unimplemented test shouldn't fail, it should be marked as pending.
A succinct method of marking a mocha test as not yet implemented is to not pass a callback function to the it handler.
describe("Traverse", function(){
describe("calls the visitor function", function(){
it("at every element inside an array")
it("at every level of a tree")
})
})
Running mocha test will display your unimplemented tests as pending.
$ mocha test
Traverse
calls the visitor function
- at every element inside an array
- at every level of a tree
0 passing (13ms)
2 pending
The official way to mark tests as not being ready for testing yet is to use skip, which is a method that appears as a field of describe and it. Here's an example:
describe("not skipped", function () {
it("bar", function () {
throw new Error("fail");
});
it.skip("blah", function () {
throw new Error("fail");
});
});
describe.skip("skipped", function () {
it("something", function () {
throw new Error("fail");
});
});
The above code, when put in a test.js file and run with $ mocha --reporter=spec test.js, produces:
not skipped
1) bar
- blah
skipped
- something
0 passing (4ms)
2 pending
1 failing
1) not skipped bar:
Error: fail
at Context.<anonymous> (/tmp/t33/test.js:3:15)
at callFn (/home/ldd/local/lib/node_modules/mocha/lib/runnable.js:223:21)
at Test.Runnable.run (/home/ldd/local/lib/node_modules/mocha/lib/runnable.js:216:7)
at Runner.runTest (/home/ldd/local/lib/node_modules/mocha/lib/runner.js:374:10)
at /home/ldd/local/lib/node_modules/mocha/lib/runner.js:452:12
at next (/home/ldd/local/lib/node_modules/mocha/lib/runner.js:299:14)
at /home/ldd/local/lib/node_modules/mocha/lib/runner.js:309:7
at next (/home/ldd/local/lib/node_modules/mocha/lib/runner.js:247:23)
at Object._onImmediate (/home/ldd/local/lib/node_modules/mocha/lib/runner.js:276:5)
at processImmediate [as _immediateCallback] (timers.js:354:15)
The test names that are preceded by - are skipped. Also, in a terminal that supports colors the tests that are skipped appear in blue (by opposition to red for failed tests and green for passing). A skipped test is said to be "pending" so Mocha reports the number of skipped tests as "2 pending".
If you pass a string or error to done() it will report it as an error. So:
it 'should not retrieve documents without an authorized user', (done) ->
done('not implemented')
would cause the test to fail with the output:
done() invoked with non-Error: not implemented
I like #Canyon's solution of just not passing a callback to mark the tests "pending", but in my case I want these placeholders to fail my CI builds, so making them actual failing tests like this was easier.
This is actually a good question because I would also find this super useful. After looking at it, I would think to just create your own wrapper "todo" or "fail" function that you can reuse throughout your codebase.
Examples below use a function called todo, which will print out the text "not yet implemented". This might be useful as a separate module even so you can import and use with all your tests. Might need to modify the code a bit...
Example in chai assert
var assert = require('chai').assert;
function todo() {
assert(false, "method not yet implemented");
};
describe("some code", function(){
it("should fail something", function(){
todo();
});
});
Example using chai assert , with the fail option (although it looks unncessary)
var assert = require('chai').assert;
function todo() {
assert.fail(true, true, "method not yet implemented"); //1st 2 args can be anything really
};
describe("some code", function(){
it("should fail something", function(){
todo();
});
});

Resources