I created a NodeJs http server in TypeScript and I've unit tested everything with Jest, except the base class, the server itself:
import { createServer} from 'http';
export class Server {
public startServer() {
createServer(async (req, res) => {
if(req.url == 'case1') {
// do case1 stuff
}
if(req.url == 'case2') {
// do case2 stuff
}
res.end();
}).listen(8080);
}
}
I'm trying this approach:
import { Server } from '../../../src/app/Server/Server';
import * as http from 'http';
describe('Server test suite', () => {
function fakeCreateServer() {
return {}
}
test('start server', () => {
const serverSpy = jest.spyOn(http, 'createServer').mockImplementation(fakeCreateServer);
const server = new Server().startServer();
expect(serverSpy).toBeCalled();
});
});
Is there a way a can create a valid fake implementation for the 'createServer' method?
And maybe simulate some requests?
Thanks a lot!
What logic do you want to test here?
Such a simple server is declarative enough to keep it without unit tests.
If you want to test that createServer is invoked
just mock http module by jest.mock('http');
Such expressions are lifted up by jest to give them precedence over regular imports.
https://jestjs.io/docs/en/mock-functions#mocking-modules
import { Server } from '../../../src/app/Server/Server';
import * as http from 'http';
jest.mock('http', () => ({
createServer: jest.fn(() => ({ listen: jest.fn() })),
}));
describe('Server', () => {
it('should create server on port 8080', () => {
const server = new Server().startServer();
expect(http.createServer).toBeCalled();
});
});
Maybe you should approach your class and tests a little bit differently.
Nodejs http.createServer returns a server instance. The server instance has a property listening (true if the server is listening for the requests), so you could return the server instance from the startServer method and test server.listening property.
Also, if you want to test for different responses and requests to your server, I would suggest you use supertest
// server.js - it also works with plain HTTP
const app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'john' });
});
module.export = app
// test.js
const request = require('supertest');
const app = require('./server.js')
describe('GET /user', function() {
it('responds with json', function(done) {
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});
Related
I want to use Fastify to create a REST API, but I want to use it with a plain Node.js http.Server instance created and configured by myself, and not use fastify.listen() for creating the socket.
With express.js, you can just pass the application handler to http.createServer() like this:
import http from "node:http";
import express from "express";
const expressApp = express();
// creating routes here
const httpServer = http.createServer(expressApp);
httpServer.listen({
port: 80,
host: "127.0.0.1",
});
I have tried that with an object created via fastify() but it does not work. Is there another way?
You need to use the serverFactory option
const http = require('http')
let server
const serverFactory = (handler, opts) => {
server = http.createServer((req, res) => {
handler(req, res)
})
return server
}
const fastify = require('fastify')({ serverFactory })
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
fastify.ready(() => {
server.listen({ port: 8080 })
})
and not use fastify.listen() for creating the socket.
You need to call fastify.ready() in any case, or the plugins will not be loaded.
I read a lot of answers to similar questions already but can't figure out what is wrong in my code.
this is my server.js file
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
app.use(require('koa-bodyparser')())
const login = (ctx, next) => {
ctx.body = ctx.request.body
}
const router = new Router({ prefix: '/api' })
router.get('/test', (ctx, next) => {
ctx.body = { resp: 'GET REQUEST /test WORKING' }
})
router.post('/login', login)
app.use(router.routes())
module.exports = app
this is my index.js file
const server = require('./server')
server.listen(3000, () => {
console.log('App is running on http://localhost:3000')
})
and this is my mocha test file
const axios = require('axios').default
const expect = require('chai').expect
const app = require('./server')
describe('7-module-3-task', () => {
describe('test', function () {
let server
before(done => {
server = app.listen(3000, done)
})
after(async () => {
server.close()
})
it('should return response from server', async () => {
const response = await axios.get('http://localhost:3000/api/test')
expect(response.data, 'should return object with key "resp').to.have.property('resp')
})
})
})
It's working okay when I make a request in Postman. I tried multiple options already but I still get 404 response, as I understand test is performed before server started running...? How can I make it working ?
First, I would move the startup (app.listen) directly into the server.js (not critical, but maybe more simpler because the require in your test would then already start your server:
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
app.use(require('koa-bodyparser')())
const router = new Router({ prefix: '/api' })
router.get('/test', (ctx, next) => {
ctx.body = { resp: 'GET REQUEST /test WORKING' }
})
app.use(router.routes())
app.listen(3000); // can be a parameter but for simplicity hardcoded here
module.exports = app
In your test you then do:
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('./server'); // this will already start up your server
describe('API Tests', () => {
describe('TEST endpoint', () => {
it('It should GET response from test endpoint', (done) => {
chai.request('http://localhost:3000')
.get('/api/test/') // be sure to have a trailing '/' !!
.end((err, res) => {
res.body.should.have.property('resp');
done();
});
})
});
});
One more hint: maybe in your original code you just have to make sure, that you have a trailing / when calling your route in the test.
Code snippets not testet but I hope you get the idea.
I shared the same code with 2 of my friends and they managed to run tests successfully.
I tested it on my other laptop after this and tests worked as well.
The problem was in the port. 3000 port was used as a default one in the debugger in Webstorm, not sure why but still.
Launching the server on port 3000 in a regular way, not in mocha tests, worked very well but in tests, it did not work, not sure why.
So for those who ever face something similar, check the default port of the debugger or any other built-in server.
I built an API for a couchbase database, using express and node.js. My problem is that when I run my tests some of them fail, because the server is not fully running. I found a solution here https://mrvautin.com/ensure-express-app-started-before-tests on how to solve this issue. The article stated that in order to solve this issue, you have to add an event emitter in your server file like this
app.listen(app_port, app_host, function () {
console.log('App has started');
app.emit("appStarted");
});
and then add this, in your test file
before(function (done) {
app.on("appStarted", function(){
done();
});
});
I have tried this, here is my implementation
Server File
app.listen(config['server']['port'], function(){
app.emit("appStarted");
logger.info("Listening")
})
Test File
before(function(done){
app.on("appStarted", function(){
done();
})
});
I keep on getting the following error
1) "before all" hook in "{root}":
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
The article is from 2016, so I was thinking that maybe the syntax has been deprecated. I was wondering if someone could please help point me in the right direction?
You can add the below condition, more info see "Accessing the main module".
if (require.main === module) {
// this module was run directly from the command line as in node xxx.js
} else {
// this module was not run directly from the command line and probably loaded by something else
}
E.g.
index.ts:
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.sendStatus(200);
});
if (require.main === module) {
app.listen(port, () => {
console.log('App has started');
});
}
export { app, port };
index.test.ts:
import { app, port } from './';
import http from 'http';
import request from 'supertest';
describe('63822664', () => {
let server: http.Server;
before((done) => {
server = app.listen(port, () => {
console.log('App has started');
done();
});
});
after((done) => {
server.close(done);
console.log('App has closed');
});
it('should pass', () => {
return request(server)
.get('/')
.expect(200);
});
});
integration test result:
(node:22869) ExperimentalWarning: The fs.promises API is experimental
63822664
App has started
✓ should pass
App has closed
1 passing (26ms)
!Hi World! My little solution here:
Check this: All depends of your testing markup...
For example, I'm using Mocha and Chai Assertion Library.
const express = require('express');
const request = require("request");
const http = require("http");
const expect = require("chai").expect;
require('dotenv').config();
describe('Server', function() {
const { PORT } = process.env;
const app = express();
before((done) => {
http.Server = app.listen(PORT, () => {
console.log(`Listening Node.js server on port: ${PORT}`);
done();
});
});
it('should return 404 response code status', () => {
const url = `http://localhost:${PORT}/api/v1/yourPath`;
return request(url, (err, response, body) => {
/* Note this result 'cause I don't have any get('/')
controller o function to return another code status
*/
expect(response.statusCode).to.equal(404);
});
})
});
I have an express server with one route. When I call this route in my browser I am able to get data sent by my server. However when I try to call this route from script with a simple node-fetch I have an response but my route is not called (not data retrieved). I explain :
Here the code of my express server:
App.ts
import express from "express";
import httpServer from "./httpServer";
import HttpRoutes from "./httpRoutes";
class BrokerServer {
public httpServer!: express.Application;
public httpRoutes!: HttpRoutes;
constructor() {
this.initHttpServer();
}
private initHttpServer(): void {
this.httpServer = httpServer;
this.httpRoutes = new HttpRoutes();
this.httpRoutes.routes();
}
}
new BrokerServer();
Server.ts
import express from "express";
import * as bodyParser from "body-parser";
class HttpServer {
public HTTP_PORT: number = 9000;
public server!: express.Application;
constructor() {
this.server = express();
this.server.use(bodyParser.json());
this.server.listen(this.HTTP_PORT, () => {
console.log('Broker HTTP Server listening on port 9000');
});
}
}
export default new HttpServer().server;
And my routes.ts
import httpServer from "./httpServer";
export default class HttpRoutes {
public routes(): void {
httpServer.get("/getNodes", (req, res) => {
console.log("GET");
res.status(200).send(JSON.stringify({ nodes: [] }));
});
}
}
When I launch my server and naviguate on the url http://localhost:9000/getNodes I can see my console.log('GET'). this is not the case when I try with node-fetch.
Here my little script:
const fetch = require('node-fetch');
console.log('launch fetch');
fetch('http://localhost:9000/getNodes')
.then(response => {
console.log('response', response);
return response.json()
})
.then(results => {
console.log('results', results);
})
.catch(error => {
console.log('error', error);
});
With the script I reach the console.log('response', response); but never my results.
Anyone knows where is the problem ?
I found why it didn't work. I used a ramdom port (port 9000) which is already used. Changed the port works...
I'm trying to integration- and unit test with Mocha, but Mocha doesn't wait for any callback to be done (or in my case, promise to fulfill)...
One moment when I try to test, the promise get's fulfilled BEFORE the first integration test is done, the other moment it crashes...
server.js
const server = require('./app');
server.listen(process.env.PORT, () => {
console.log('Starting server.');
console.log('Displaying server information:');
console.log(`Host: http://localhost:${process.env.PORT}`);
});
app.js
require('babel/register');
/**
* Make some commonly used directories known to the process env
*/
process.env.ROOT_DIR = __dirname;
/**
* Setup Express server and the Express Router
*/
const express = require('express');
const bodyParser = require('body-parser');
const server = express();
// Parses the body for JSON object in the middleware so extra steps for
// parsing aren't required.
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({extended: true}));
server.use((req, res, next) => {
console.log('url called', req.originalUrl);
next();
});
// This is the call which has the database set up in it
// This has a few generator functions in 'm
require('./init')(server);
/**
* Export
*/
// Define routes on the server.
module.exports = server;
integration test (which is the first one that gets fired)
import should from 'should';
import supertest from 'supertest';
import server from '../../app';
import co from 'co';
let request = supertest(server); // Set request to context of the app.
describe('Index', function () {
describe('GET /', function () {
it('returns 200', co.wrap(function * (done) {
const res = yield request
.get('/');
res.statusCode.should.equal(200);
}));
});
describe('GET /this_is_not_a_valid_route_123456', function () {
it('returns 404', function (done) {
request
.get('/this_is_not_a_valid_route_123456')
.expect(404, done);
});
});
});
command:
NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- ./test/ --recursive --compilers js:babel/register
Does anyone know how I can fix this?
Screenshot of what happens:
(Init done should be BEFORE the 'Index GET /' and directly after 'Init config repo...')
You can tell mocha to wait.
before(function (done) {
if (server.listening) return done();
server.on('listening', function() { done(); });
});
I'm not sure what's inside your init file but if you also want to wait for other events, you can add multiple before functions.