How to utilize Request to integration test async Koa Node API - javascript

I'm working on my first real-world Node project using Koa2 and Request for making RESTful API calls to a 3rd party. The Koa service itself is relatively simple, but I'm trying to write an integration test for it using Jest. I've found examples of using Jest with Supertest/Superagent, but I cant find how I'd write the equivalent test using ONLY Jest and Request as the http client. Below is the Jest/Supertest example...
const request = require('supertest');
const app = require('../../src/app')
describe('Test the root path', () => {
test('It should response the GET method', async () => {
const response = await request(app).get('/');
expect(response.statusCode).toBe(200);
});
})
It seems like I should be able to just use Request to do the same thing that supertest/superagent is doing here, but I cant find any example. Thanks for suggestions!

Supertest looks magical because you can pass it your app and somehow that just works.
Under the hood, Supertest justs starts listening and configures the underlying request to use the address as a base url.
I'm using Axios here as an example, I don't use Request but it should be easy enough to adjust.
const axios = require('axios')
const app = require('../../src/app')
const server = app.listen() // random port
const url = `http://127.0.0.1:${server.address().port}`
const request = axios.create({ baseURL: url })
describe('Test the root path', () => {
test('It should response the GET method', async () => {
const response = await request.get('/')
expect(response.statusCode).toBe(200)
});
})

Related

how to fetch local api in react.js

I'm a new one, I'm trying to fetch my own api on local,this what I do:
in "src/server.js"
const express = require("express");
const app = express();
app.get("/api", (request, response) => {
response.sendFile("api.js", { root: __dirname });
});
app.listen(8000);
then I use creat-react-app to create a react project, how can I call my api in App.js?
if my situation is not complete, please tell me.
Q1:with server.js, after I run "node server.js", I can can call this file in browser that means I have done an api, right?
Q2:how can I get value from my api.js in App.js without cors problem?
Firstly you should add your request address into package.json file. For example if you want to send http request to https://localhost:5000/, you should add "proxy":"http://localhost:5000" line into your package.json file.
After adding this line, you can send http request without getting CORS error.
Also you can use fetch function to sending request and getting response from server side in your react code(App.js).
fetch('/api')
.then((response) => response.json())
.then((data) => console.log(data));

Is it normal that I am able to invoke firebase cloud functions from any kind of frontend app?

I am using axios to call the firebase cloud functions I have created with express/firebase. I realized even without using and without importing the firebase and without initializeApp(firebaseConfig) in my frontend, I was able to call my cloud functions with the axios with the following way:
axios.get('https://us-central1...')
How I create cloud functions:
index.js
module.exports ={
...require('./controllers/foo')
}
foo.js
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const app = express();
//Middleware
app.use(express.json());
app.use(cors({ origin: true}));
app.get('/', async (req, res) => {
// Function goes in here
});
app.post('/', async (req, res) => {
// Function goes in here
});
app.put('/:id', async (req, res) => {
// Function goes in here
});
app.delete('/:id', async (req, res) => {
// Function goes in here
});
exports.foo = functions.https.onRequest(app);
Is this a normal behavior or is it due to way of working of express (cors as middleware) or axios somehow? Or do I just have to add auth and firestore rules into my database? Whatever the reason is then what is the meaning of the firebaseConfig information that firebase provides for us?
PS: Just to play around I have tried to add firestore rules. I have added the following rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Even though in rules playground I was not able to retrieve anything, from my application I still got the query I wanted and I don't know why.
Yes that is absolutely normal. The HTTP Functions are made so you can integrate you Firebase Project with any (absolutely any) other language or platform by using HTTP requests as the trigger name shows. As you already do, you can even implement express apps behind those requests.
With those you gave full power and responsebility what goes trough them and with that comes also the need for you to know who is calling your HTTP requests. if you want to secure them you can use the link from the comment and check how to make Authorized HTTP Ednpoinds.
If you want to call those just from your App Frontend I would recommend to use Callable Firebse Functions because those will work only from your app and also provide the data of the user who called them in the context of your cloud function triggered.

How to start an express server using testEnvironment as NodeEnvironment?

After looking into the questions:
Async setup of environment with Jest
window/document not defined in import
Configure Jest global tests setup with .ts file (TypeScript)
About app.listen() callback
How to write a Jest configuration file
NodeJS: How to get the server's port?
https://alligator.io/nodejs/serving-static-files-in-express/
Promisify server.listen
Unexpected token import in custom node environment
How to access class properties of Jest Test Environment inside child test?
Cannot create custom TestEnvironment in Jest
globalSetup is executed in different context than tests
global beforeAll
How to test url change with Jest
Specify window.location for each test file for Jest
window.location.href can't be changed in tests
global beforeAll
How do I test a single file using Jest?
https://basarat.gitbook.io/typescript/intro-1/jest
I was able to do this:
package.json
{
"name": "my-project",
"jest": {
"testEnvironment": "./testEnvironment.js",
}
}
testEnvironment.js
const express = require('express');
// const NodeEnvironment = require('jest-environment-node'); // for server node apps
const NodeEnvironment = require('jest-environment-jsdom'); // for browser js apps
class ExpressEnvironment extends NodeEnvironment {
constructor(config, context) {
super(config, context);
}
async setup() {
await super.setup();
const app = express();
this.global.server = app.listen(0, "127.0.0.1", () => {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
how to make setup() wait until app.listen callback is finished,
i.e., the server has properly started.
});
app.use(express.static('../testfiles'));
}
async teardown() {
this.global.server.close();
await super.teardown();
}
runScript(script) {
return super.runScript(script);
}
}
module.exports = ExpressEnvironment;
How to make setup() wait until app.listen() callback is finished, i.e., the server has properly started?
Before, when I was using beforeAll(), my code was working fine because I could use the done() async callback passed by beforeAll():
const express = require('express');
const app = express();
var server;
beforeAll(async (done) => {
server = app.listen(0, "127.0.0.1", () => {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
done();
});
app.use(express.static('../testfiles'));
});
afterAll(() => {
server.close();
});
How would be the equivalent to the beforeAll done() callback on the NodeEnvironment setup() function?
You can do this by awaiting the listen even, wrapping it in a promise, and calling the promise resolve as the callback to the server listen
const app = express();
let server;
await new Promise(resolve => server = app.listen(0, "127.0.0.1", resolve));
this.global.server = server;
You could also put a custom callback that will just call the promise resolver as the third argument to the app.listen() and it should run that code then call resolve if you need some sort of diagnostics.
Extending Robert Mennell answer:
You could also put a custom callback that will just call the promise resolver as the third argument to the app.listen() and it should run that code then call resolve if you need some sort of diagnostics.
let server;
const app = express();
await new Promise(function(resolve) {
server = app.listen(0, "127.0.0.1", function() {
console.log(`Running express server on '${JSON.stringify(server.address())}'...`);
resolve();
});
});
this.global.server = server;
Then, you can access the this.global.server in your tests files to get the server port/address: Is it possible to create an Express.js server in my Jest test suite?

how to mock node-redis using jest

i am using jest and trying to mock node-redis using redis-mock.
// redis.js
const redis = require("redis");
const client = redis.createClient({ host: '127.0.0.1', port: 6379 });
// redis.test.js
const redisMock = require("redis-mock");
describe("redis", () => {
jest.doMock("redis", () => jest.fn(() => redisMock));
const redis = require("./redis");
});
when i run the tests, i get an error
TypeError: redis.createClient is not a function
feels to me that i am doing something wrong when using jest.doMock().
would appreciate your assistance.
The following works for me:
import redis from 'redis-mock'
jest.mock('redis', () => redis)
I do not include the redis library in the test at all, but I include it only in the file which contains the tested class. (The file with tested class is, of course, included in the test file.)
If you are using jest, the switching of the mock and the actual implementation is possible automatically. Works great for CI.
jest.config.js
module.exports = {
// other properties...
setupFilesAfterEnv: ['./jest.setup.redis-mock.js'],
};
jest.setup.redis-mock.js
jest.mock('redis', () => jest.requireActual('redis-mock'));

How to route nodejs requests to another nodejs application?

Hi I am trying to build a really simple "API-gateway" to demonstrate a small scale microservice project. I am using Nodejs and Express and I wanted to write a really simple public facing api-gateway server to route requests to my different microservices. For example lets say I have microservice A B and C. I wanted requests to localhost:3000/api/A to go to microservice A and return the result and then all other requests to localhost:3000/api/B go to microservice B and return the result ect. I wanted to write this instead of using NGINX, could someone help me understand how I can achieve this? (Most of my other "microservices" are also nodejs/express apis)
Could I get a quick simple example in code? I would love to see a GET request to google and then the client be able to get the GET request. (use of other libraries or modules would be cool too! :D)
You can run B on port 3001, C on 3002.
Then dispach all request by A on port 3000.
You can use Http client like axios in A to request B or C.
Example
A.js
const express = require('express')
const axios = require('axios')
const app = express()
app.get('/api/B', (req, res) => {
axios.get('http://localhost:3001/dosomething', {
params: {...req.query, name: 'Device A'}, //Fetch datas from params
}).then(result => {
res.send(result)
})
})
app.get('/api/C', (_, res) => {
axios.get('http://localhost:3002/dosomething').then(result => {
res.send(result)
})
})
app.listen(3000, () => console.log('On 3000'))
B.js
const express = require('express')
const app = express()
app.get('/dosomething', (req, res) => {
const data = req.query
//Do something with data fetched here you know...
res.send('No worry, I will do something for ' + data.name)
})
app.listen(3001, () => console.log('On 3001'))
If all micro-services are deployed on the same machine(different machines just need a bit more syncing to know ports/ips but that should not be an issue), you just use a common file to store ops/ports and then just redirect calls from route ip:3000/api/A to ipA:portA

Categories