NestJS + Jest: how to receive external http requests within integration test? - jestjs

Goal
In my setup I have two applications. Application A (from a third party ) receives user input, processes it and then sends the processed data via POST to Application B (my nestjs server). I want to run an integration test, where I want to validate internal variables of Application B against User input. For this I want to define the tests within B which do the following steps:
send predefined user input to A
receive a POST from A
do some processing
validate the processing
Defining the tests within B should give the possibility to examine code coverage for the integration tests. I can't do this if I write another application which tests from external.
Problem
I didn't find any way how to receive external http requests within jest tests. My approach would be to create a module with a controller and then listen to the port. For example like this
describe('Integration Test', () => {
beforeAll(async () => {
const testingModule: TestingModule = await Test.createTestingModule(
{
imports: [HttpModule],
providers: [IntegrationTestService],
controllers: [IntegrationTestController],
},
).compile();
await testingModule.listen(3000);
// here I define my tests
});
}
But TestingModule doesn't provide the function "listen". I also didn't get it working using a predefined normal module that is created via NestFactory.create(IntegrationTestModule).
Any suggestion is appreciated!

To run the test server on a port, you need to call testingModule.createNestApplication() like it shows in the docs under e2e tests. Adding this to the end of your beforeAll() will allow you to receive the http requests
const app = testingModule.createNestApplicaiton();
await app.listen(3000);

Related

Jest run a single test to get Authentication token before running all tests specified across multiple.test.ts files

I am using Jest to automate the testing of a specific API that involves multiple endpoints. The way I have structured my framework is that I have a specific request spec class and test.ts file (i.e. test suite) for each of the endpoints. The calls to these endpoints are authenticated with a token. So in each of the .test.ts test suite files I am using the below beforeAll hook to get the token that is used in each of my tests within the test suite.
beforeAll(async () => {
pricesRequest = new PricesRequestSpec();
tokenRequest = new TokenRequestSpec();
token = await tokenRequest.postTokenRequest();
});
This is fine in that I am only retrieving and using 1 token for all tests in a test suite rather than for each test, however as I have 6 endpoints with the tests for each of these in 6 separate .test.ts files I am making 6 requests for a new token when I really only need to make 1. Is there a way to run the request to retrieve the token before executing any of the testsuites so that I can make just 1 call for get token rather than multiple i.e. using some globalConfig. I know I could get around this by having all tests in the single test.ts testSuite file but for readability purposes I want to keep these in separate folders.
Much appreciated.
I worked out how to do this. I created a script globalSetup.ts in which I make the call to get the token and set this as an env variable..
export default async () => {
let tokenRequest = new TokenRequestSpec();
const token = await tokenRequest.postTokenRequest();
process.env.bearerToken = token;
}
I point to this file in my jest.config.ts for the param...
globalSetup: './jestGlobalSetup.ts'
I can then access this environment varaibles within all my test scripts e.g.
it('returns status 200 and expected response', async () => {
const response = await pricesRequest.getPricesRequest(process.env.bearerToken, queryParams);
pricesRequest.validateSuccessfulResponse(response);
})

Node.JS: Refreshing the nodemailer-mock after each test in Jest

I am building a Node JS application using Express.js framework. My application is sending email using the nodemailer package, https://nodemailer.com/about/. I am writing test using supertest and Jest. I am using nodemailer-mock package, https://www.npmjs.com/package/nodemailer-mock to mock my application.
I can mock the logic for sending email for Jest as mentioned in the nodemailer-mock documentation. The only issue is that it is not resetting the data/ count after each test. For example, I have two tests and both are sending emails and when I do the following assertion, it is failing.
const sentEmails = mock.getSentMail();
// there should be one
expect(sentEmails.length).toBe(1);
It is failing because an email was sent in the previous test. How can I reset it?
Assuming your mock variable is set (or even imported) at the module level, this should cover it:
afterEach(() => {
mock.reset();
});
Keep in mind that this method also seems to reset anything else you have configured on the mock (success response, fail response, etc.), so you may need to perform that setup each time too. That would be a good fit for a beforeEach block.

How can I create a default item in my database with Jest so that I could test my API CRUD operations?

I have a complete API build with express that can handle all CRUD operations. My api handles the creation of Services. I'm using an a MYSQL database with my API which contains one table called service. Now I want to be able to insert a mock/default service object so that I could run that same service object in all my tests, and delete it at the end so that I don't fill the my service table with redundant data. And I want that service object to be put through my almost all my tests except my Post request. How can I accomplish this with Jest?
Here's what I have so far in my service.test.js file
How can I create the service object before all my tests and delete it after all my tests and keep it as a global variable to my file?
Jest provides beforeAll and afterAll for your problem/situation
beforeAll - Function will run before all the tests.
afterAll - Function will run after all the tests
Code Structure:
...
beforeAll((done) => {
//Insert your mock data on database
});
afterAll((done) => {
//Delete your mock data from database
});
test('', async() => {
//Your test with database mock data
});
....
Reference : Setup and Teardown -> One-Time Setup

React app with Server-side rendering crashes with load

I'm using react-boilerplate (with react-router, sagas, express.js) for my React app and on top of it I've added SSR logic so that once it receives an HTTP request it renders react components to string based on URL and sends HTML string back to the client.
While react rendering is happening on the server side, it also makes fetch request through sagas to some APIs (up to 5 endpoints based on the URL) to get data for components before it actually renders the component to string.
Everything is working great if I make only several request to the Node server at the same time, but once I simulate load of 100+ concurrent requests and it starts processing it then at some point it crashes with no indication of any exception.
What I've noticed while I was trying to debug the app is that once 100+ incoming requests begin to be processed by the Node server it sends requests to APIs at the same time but receives no actual response until it stops stacking those requests.
The code that's used for rendering on the server side:
async function renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames }) {
// 1st render phase - triggers the sagas
renderAppToString(store, renderProps);
// send signal to sagas that we're done
store.dispatch(END);
// wait for all tasks to finish
await sagasDone();
// capture the state after the first render
const state = store.getState().toJS();
// prepare style sheet to collect generated css
const styleSheet = new ServerStyleSheet();
// 2nd render phase - the sagas triggered in the first phase are resolved by now
const appMarkup = renderAppToString(store, renderProps, styleSheet);
// capture the generated css
const css = styleSheet.getStyleElement();
const doc = renderToStaticMarkup(
<HtmlDocument
appMarkup={appMarkup}
lang={state.language.locale}
state={state}
head={Helmet.rewind()}
assets={assets}
css={css}
webpackDllNames={webpackDllNames}
/>
);
return `<!DOCTYPE html>\n${doc}`;
}
// The code that's executed by express.js for each request
function renderAppToStringAtLocation(url, { webpackDllNames = [], assets, lang }, callback) {
const memHistory = createMemoryHistory(url);
const store = createStore({}, memHistory);
syncHistoryWithStore(memHistory, store);
const routes = createRoutes(store);
const sagasDone = monitorSagas(store);
store.dispatch(changeLocale(lang));
match({ routes, location: url }, (error, redirectLocation, renderProps) => {
if (error) {
callback({ error });
} else if (renderProps) {
renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames })
.then((html) => {
callback({ html });
})
.catch((e) => callback({ error: e }));
} else {
callback({ error: new Error('Unknown error') });
}
});
}
So my assumption is that something is going wrong once it receives too many HTTP requests which in turn generates even more requests to API endpoints to render react components.
I've noticed that it blocks event loop for 300ms after renderAppToString() for every client request, so once there are 100 concurrent requests it blocks it for about 10 seconds. I'm not sure if that's a normal or bad thing though.
Is it worth trying to limit simultaneous requests to Node server?
I couldn't find much information on the topic of SSR + Node crashes. So I'd appreciate any suggestions as to where to look at to identify the problem or for possible solutions if anyone has experienced similar issue in the past.
In the above image, I am doing ReactDOM.hydrate(...) I can also load my initial and required state and send it down in hydrate.
I have written the middleware file and I am using this file to decide based on what URL i should send which file in response.
Above is my middleware file, I have created the HTML string of the whichever file was requested based on URL. Then I add this HTML string and return it using res.render of express.
Above image is where I compare the requested URL path with the dictionary of path-file associations. Once it is found (i.e. URL matches) I use ReactDOMserver render to string to convert it into HTML. This html can be used to send with handle bar file using res.render as discussed above.
This way I have managed to do SSR on my most web apps built using MERN.io stack.
Hope my answer helped you and Please write comment for discussions
1. Run express in a cluster
A single instance of Node.js runs in a single thread. To take
advantage of multi-core systems, the user will sometimes want to
launch a cluster of Node.js processes to handle the load.
As Node is single threaded the problem may also be in a file lower down the stack were you are initialising express.
There are a number of best practices when running a node app that are not generally mentioned in react threads.
A simple solution to improve performance on a server running multiple cores is to use the built in node cluster module
https://nodejs.org/api/cluster.html
This will start multiple instance of your app on each core of your server giving you a significant performance improvement (if you have a multicore server) for concurrent requests
See for more information on express performance
https://expressjs.com/en/advanced/best-practice-performance.html
You may also want to throttle you incoming connections as when the thread starts context switching response times drop rapidly this can be done by adding something like NGINX / HA Proxy in front of your application
2. Wait for the store to be hydrated before calling render to string
You don't want to have to render you layout until your store has finished updating as other comments note this is a blocks the thread while rendering.
Below is the example taken from the saga repo which shows how to run the sagas with out the need to render the template until they have all resolved
store.runSaga(rootSaga).done.then(() => {
console.log('sagas complete')
res.status(200).send(
layout(
renderToString(rootComp),
JSON.stringify(store.getState())
)
)
}).catch((e) => {
console.log(e.message)
res.status(500).send(e.message)
})
https://github.com/redux-saga/redux-saga/blob/master/examples/real-world/server.js
3. Make sure node environment is set correctly
Also ensure you are correctly using NODE_ENV=production when bundling / running your code as both express and react optimise for this
The calls to renderToString() are synchronous, so they are blocking the thread while they are running. So its no surprise that when you have 100+ concurrent requests that you have an extremely blocked up queue hanging for ~10 seconds.
Edit: It was pointed out that React v16 natively supports streaming, but you need to use the renderToNodeStream() method for streaming the HTML to the client. It should return the exact same string as renderToString() but streams it instead, so you don't have to wait for the full HTML to be rendered before you start sending data to the client.

In Loopback.js, how to run a standalone script without running the application?

I'm running a standalone script to manipulate some persisted data in my Loopback application. I need to do this on production servers while the production app is running. I have it working like this:
Filepath is ./scripts/my-script.js and looks like this:
'use strict';
const app = require('../server/server');
const Account = app.models.Account;
Account.find()
.then(accounts => {
// do data stuff with accounts
})
.then(() => process.exit())
.catch(error => {
console.error(error.stack);
process.exit(1);
});
Then I run node ./scripts/my-script.js.
The problem is the entire application including the web server, boot scripts, etc, runs for the duration of the script, which means I'd have two instances of the app running. This happens because I get reference to the Account model like this:
const app = require('../server/server');
const Account = app.models.Account;
When I try:
const loopback = require('loopback');
const Account = loopback.getModel('Account');
It fails with:
./node_modules/loopback/lib/registry.js:304
throw new Error('Model not found: ' + modelName);
^
Error: Model not found: Account
How can I run this script (or perhaps use a different approach) to manipulate persisted data without running a second instance of the application?
What's your end goal here? You could use the loopback-connector-remote to hit the same app instance through the REST API.
You could also perform operations on the same db through the second app instance.
A third option would be to create a model of class Model (instead of PersistedModel), define its datasource as null, and instead of running a second nodejs process, you could execute the script by hitting the associated endpoint. I would just make sure you use a good security policy for that specific endpoint.

Resources