How to test the bad cases? - node.js

Given that there are plenty of libraries for generating mock/fake data starting from a JSON schema or a Mongoose model and so on, how to test the behavior of a sw (let's say a web api) for the wrong data cases.
For example let's say I have a user service with name, email, phone, etc and a deep description of its fields data content. Let's say I have also different error codes that should be thrown for each possible error. I can't understand how to programmatically produce bad mock data (wrong email, bad formed name, etc) to put inside a test.
Moreover are there some test-process specs for these cases?
Thanks!
---- Integration for to answer to TGW: I'm doing standard tests, something like:
const app = require(-myapp-); //express or whatever app
const userModel = require('-myModelDefinition-); //e.g. json schema
const mockingLibrary = require(-mockingLibrary-); //Sinon or any other
//I'll test that the user->create method works properly on good input
const fakeUser = mockingLibrary.mock(userModel);
describe('\'users\' service', () => {
it('create method - good case',()=>{
return app.service('users')
.create(fakeUser)
.should.eventually
.have.property('_id'); //or any other test
});
});
//What if I want to test on bad input?
const fakeUserWithErrorOnEmail = mockingLibrary.mock(userModel,{errorOn:'email'});
describe('\'users\' service', () => {
it('create method - bad email case',done=>{
return app.service('users')
.create(fakeUserWithErrorOnEmail)
should.be.rejectedWith(Error, "email-error-message");
});
});
So what I'm looking for, maybe, is a mocking library able to produce both good and bad mock data given the description/schema of good ones.

Related

Find a class instance inside a function (JS) or how to write good self-documented code

Probably everyone was facing the issue when Postman documentation will be barely up-to-date with the code currently running in dev or production.
Almost every time the source of that problem is "a little change" or adding a new column into the database.
So I would like the code to be self-documented, starting from the validator.
There is the wrap-implementation over the JOI validator
class Validator {
_schema = {};
static TYPES = Joi;
setRule(field, rule){
this._schema[field] = rule;
}
validate(data){
let schema = Joi.object(this._schema);
try {
Joi.assert(data, schema, {abortEarly: false});
}
catch(error){
const message = error.details.map(detail => detail.message);
throw new ValidationError(message);
}
}
}
The controller uses this wrap just like that
const validator = new Validator();
validator.setRule('sts', Validator.TYPES.string());
validator.validate(body);
Every time the crucial code change happens, it usually affects the incoming body validation.
So the idea behind self-documentation is why not to use validator class as a source of the API documentation? We could get almost every piece of information about the endpoint using router + validator.
But how can you collect the data about that for every endpoint (especially, controller).
We should somehow get access to every instance of the Validator class inside each controller?
Or shall we just read the source file to get an information about the rules?
There are several implementations of Swagger-like docs, but they are all based on decorators code style, from which we are trying to get rid of.

Error snap.data is not a function with firebase cloud function and onCreate

I have a cloud function that sends a welcome email every time a new user registers in the database.
The function correctly executes everything, sends the emails and these are received by the recipient, so far, everything is fine.
It works when I manually write the email address in the function, but when I want it to get the data from the realtime database, it gives me the error:
TypeError: snap.data is not a function
This is the code of my function:
const functions = require('firebase-functions');
const nodemailer = require("nodemailer");
const transport = nodemailer.createTransport({
service: "Gmail",
auth: {
user: "MY_EMAIL",
pass: "MY_EMAIL_PASSWORD"
}
})
exports.welcomeMail = functions.database.ref('/paso1/{id}').onCreate((snap, context) => {
const _name = snap.data().name;
return sendWelcomeMail(_name)
});
// aux functions
function sendWelcomeMail(name) {
return transport.sendMail({
from: "JohnDoe <sender#test.com>",
to: "myfriendemail#gmail.com",
subject: "Hello",
html: `
<h1<Hello ${name} </h1>
<p>nice to seeyou</p>
`
})
.then(r => r)
.catch(e => e);
}
This is my realtime database:
I have reviewed the documentation several times, I have tested with snap.val().{uid}; but all without success, I cannot recover the "name" field from the database.
Using const _name = snap.val().name; I get the same error
I am not sure what is failing.
The method you're looking for is snap.val(), not snap.data(). You might be confusing Realtime Database with Firestore. Firestore uses data() to get the raw data out of a DocumentSnapshot, but that's different than Realtime Database.
You have a typo. You declare snap and then refer to it as snapshot. To fix this problem, make sure the declaration and use match.
You're also using snapshot.data(), while data() doesn't exist on a Realtime Database snapshot (you're probably confusing it with Cloud Firestore).
So combining those two fixes, this should be much closer:
exports.welcomeMail = functions.database.ref('/paso1/{id}')
.onCreate((snapshot, context) => { // this line changed
const _name = snapshot.val().name;
...
I finally found what the mistake was.
Indeed, as you have indicated to me, the correct way to extract the data from the realtime database is by using .val()
However, I told you in the comments to the answers that I kept returning error.
It didn't work because I wasn't initializing the firebase SDK as an ADMIN, necessary to access, among other things, the realtime database.
https://firebase.google.com/docs/admin/setup
I hope my mistake will save other programmers time.
Thanks to all for the help

What is the reason for using GET instead of POST in this instance?

I'm walking through the Javascript demos of pg-promise-demo and I have a question about the route /api/users/:name.
Running this locally works, the user is entered into the database, but is there a reason this wouldn't be a POST? Is there some sort of advantage to creating a user in the database using GET?
// index.js
// --------
app.get('/api/users/:name', async (req, res) => {
try {
const data = (req) => {
return db.task('add-user', async (t) => {
const user = await t.users.findByName(req.params.name);
return user || t.users.add(req.params.name);
});
};
} catch (err) {
// do something with error
}
});
For brevity I'll omit the code for t.users.findByName(name) and t.users.add(name) but they use QueryFile to execute a SQL command.
EDIT: Update link to pg-promise-demo.
The reason is explained right at the top of that file:
IMPORTANT:
Do not re-use the HTTP-service part of the code from here!
It is an over-simplified HTTP service with just GET handlers, because:
This demo is to be tested by typing URL-s manually in the browser;
The focus here is on a proper database layer only, not an HTTP service.
I think it is pretty clear that you are not supposed to follow the HTTP implementation of the demo, rather its database layer only. The demo's purpose is to teach you how to organize a database layer in a large application, and not how to develop HTTP services.

How to generate API html documentation from mocha BDD tests?

I worked on a project with Spring Boot java framework where guys automated API docs generation. Every time you run BDD/Integration style tests, there was api blue print file created out from mocha tests. Then it ran generate-html-from-api blueprint. I liked this approach as it has two advantages:
1) API docs are always correct and up-to-date
2) saves time, because no need to write another documentation file (like apidoc).
Has anyone tried and has working example for node projects? I found api-doc-test plugin, however its documentation is limited. ? Ideally, I would like just to run:
mocha --recursive
Which would generate api-doc.html and place under test/tmp/.
I have looked at swagger but I really don't want to specify endpoint information twice and it would really be awesome just write once in BDD tests and have double result (tests + docs) at the same time.
https://github.com/stackia/test2doc.js
I'm working on this project, which enables generating documents (currently only API blueprint) from BDD tests, just exactly what you need.
Test code example:
const doc = require('test2doc')
const request = require('supertest') // We use supertest as the HTTP request library
require('should') // and use should as the assertion library
// For Koa, you should exports app.listen() or app.callback() in your app entry
const app = require('./my-express-app.js')
after(function () {
doc.emit('api-documentation.apib')
})
doc.group('Products').is(doc => {
describe('#Products', function () {
doc.action('Get all products').is(doc => {
it('should get all products', function () {
// Write specs towards your API endpoint as you would normally do
// Just decorate with some utility methods
return request(app)
.get(doc.get('/products'))
.query(doc.query({
minPrice: doc.val(10, 'Only products of which price >= this value should be returned')
}))
.expect(200)
.then(res => {
body = doc.resBody(res.body)
body.desc('List of all products')
.should.not.be.empty()
body[0].should.have.properties('id', 'name', 'price')
body[0].price.desc('Price of this product').should.be.a.Number
})
})
})
})
})

How to mock external service when testing a NodeJS API

I have JSON API built with koa which I am trying to cover with integration tests.
A simple test would look like this:
describe("GET: /users", function() {
it ("should respond", function (done) {
request(server)
.get('/api/users')
.expect(200, done);
});
});
Now the issue comes when the actions behind a controller - lets say saveUser at POST /users - use external resources. For instance I need to validate the users phone number.
My controller looks like this:
save: async function(ctx, next) {
const userFromRequest = await parse(ctx);
try {
// validate data
await ctx.repo.validate(userFromRequest);
// validate mobile code
await ctx.repo.validateSMSCode(
userFromRequest.mobile_number_verification_token,
userFromRequest.mobile_number.prefix + userFromRequest.mobile_number.number
);
const user = await ctx.repo.create(userFromRequest);
return ctx.data(201, { user });
} catch (e) {
return ctx.error(422, e.message, e.meta);
}
}
I was hoping to be able to mock the ctx.repo on the request object but I can't seem to able to get a hold on it from test, which means that my tests are actually hitting the phone number verification service.
Are there any ways I could go around hitting that verification service ?
Have you considered using a mockup library like https://github.com/mfncooper/mockery?
Typically, when writing tests requiring external services, I mock the service client library module. For example, using mocha:
mockery = require('mockery');
repo = require('your-repo-module');
before(function() {
mockery.enable();
repo.validateSMSCode = function() {...};
mockery.registerMock('your-repo-module', repo);
}
This way, every time you require your-repo-module, the mocked module will be loaded rather than the original one. Until you disable the mock, obviously...
app.context is the prototype from which ctx is created from. You may
add additional properties to ctx by editing app.context. This is
useful for adding properties or methods to ctx to be used across your
entire app, which may be more performant (no middleware) and/or easier
(fewer require()s) at the expense of relying more on ctx, which could
be considered an anti-pattern.
app.context.someProp = "Some Value";
app.use(async (ctx) => {
console.log(ctx.someProp);
});
For your sample your re-define app.context.repo.validateSMSCode like this, assuming that you have following setup lines in your test:
import app from '../app'
import supertest from 'supertest'
app.context.repo.validateSMSCode = async function(ctx, next) {
// Your logic here.
};
const request = supertest.agent(app.listen())
After re-defining app.context.repo.validateSMSCode method that your will define in your test, will work, instead of original method.
https://github.com/koajs/koa/blob/v2.x/docs/api/index.md#appcontext
https://github.com/koajs/koa/issues/652

Resources