Jest: ReferenceError: Request is not defined - api

I can't get my jest test to pass even though the code works:
describe("Testing the API call", () => {
test("Testing the API call", () => {
sendToServer("Hey there!")
})
})
And jest throws me this:
ReferenceError: Request is not defined (it can't find Request constructor)
I'm pretty new to jest so I have only tried what I could find on stack overflow, but there are no solutions to this one. I tried to import Request from html and it didn't work.

It would be easier to help you if you would share your getData function, but let me assume that you are doing something like, for fetching your data:
async function getUsers() {
const URL = `https://randomuser.me/api/?results=10`;
try {
const response = await fetch(URL);
return response.json();
}
catch (e) {
console.log(e);
return {}
}
}
The above function will make a call to the Random User API and return an Object with an array of results, in our case 10 random users.
In order to test this function, you can write tests, such as:
describe('getUsers function', () => {
test('the data returned is an object', async () => {
const data = await index.getUsers();
expect(data).toBeInstanceOf(Object);
});
test('the Results array has 10 entries', async () => {
const data = await index.getUsers();
expect(data.results.length).toBe(10)
});
});
Here you will assert, that you do return Object from the call and that you do return correct number of users on call.

Related

Test Express.js routes which rely on a TypeORM connection

I have a very basic Express.js app which I use Jest and Supertest to test. The routes are not set up until the database is connected:
class App {
public app: express.Application;
public mainRoutes: Util = new Util();
constructor() {
this.app = express();
AppDataSource.initialize()
.then(() => {
// add routes which rely on the database
this.mainRoutes.routes(this.app);
})
.catch((error) => console.log(error));
}
}
export default new App().app;
Here is my test:
describe("Util", function () {
test("should return pong object", async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
});
});
Since I put in the promise, this has been 404ing. I can't add async to the constructor. I tried refactoring the class to separate the connection with setting up the routes, but it didn't seem to help.
This works:
test("should return pong object", async () => {
setTimeout(async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
}, 1000);
});
But obviously I don't want to add a setTimeout. How is this usually done? I am new to testing.
Just remove the setTimeout() and await the call to the application. You should be initializing the application in the beforeAll() method, which I assume you have, to get the application up and running in the testing space. You should also mock your database connection, so you can fake the data you want back, and not have to wait for the external database to actually be available.
// Create a mock for your database, and have it return whatever you need
import <your-database-class> = require('database');
jest.mock('database', () => {
...
});
describe("Util", function () {
beforeAll(async () => {
app = await <whatever you do to launch your application>
});
test('should be defined', () => {
expect(app).toBeDefined();
});
test("should return pong object", async () => {
const res = await request(app).get("/ping");
expect(res.statusCode).toEqual(200);
expect(res.body).toEqual({ message: "pong" });
});
});

Jest does not continue after async method

I have an async method triggered by a click event where I make a call to an API and then process the response, like this:
async confirmName () {
const {name, description} = this.form;
const [data, error] = await Pipelines.createPipeline({name, description});
if (error) {
console.error(error);
this.serviceError = true;
return false;
}
this.idPipelineCreated = data.pipeline_id;
return true;
}
The test looks like this:
test("API success", async () => {
const ConfirmNameBtn = wrapper.find(".form__submit-name");
await ConfirmNameBtn.vm.$emit("click");
const pipelinesApi = new Pipelines();
jest.spyOn(pipelinesApi, "createPipeline").mockResolvedValue({pipeline_id: 100});
const {name, description} = wrapper.vm.form;
pipelinesApi.createPipeline().then(data => {
expect(wrapper.vm.pipelineNameServiceError).toBe(false);
wrapper.setData({
idPipelineCreated: data.pipeline_id
});
expect(wrapper.vm.idPipelineCreated).toBe(data.pipeline_id)
}).catch(() => {})
})
A basic class mock:
export default class Pipelines {
constructor () {}
createPipeline () {}
}
I'm testing a success API call and I mock the API call returning a resolved promised. The problem is the coverage only covers the first two lines of the method, not the part where I assign the response of the API call. Is this the correct approach?
Edit:
Screenshot of coverage report:
Don't mix up await and then/catch. Prefer using await unless you have very special cases (see this answer):
test("API success", async () => {
const ConfirmNameBtn = wrapper.find(".form__submit-name");
await ConfirmNameBtn.vm.$emit("click");
const pipelinesApi = new Pipelines();
jest.spyOn(pipelinesApi, "createPipeline").mockResolvedValue({pipeline_id: 100});
const {name, description} = wrapper.vm.form;
const data = await pipelinesApi.createPipeline();
expect(wrapper.vm.pipelineNameServiceError).toBe(false);
wrapper.setData({
idPipelineCreated: data.pipeline_id
});
expect(wrapper.vm.idPipelineCreated).toBe(data.pipeline_id)
expect(wrapper.vm.serviceError).toBe(false);
})

In reactnative expo I tried using secureStore from expo in redux to save token the one I get from api

I tried using redux to save token the one I get from api in react native ..its working now.
First one is for settoken and other one is for gettoken.
enter image description here
export const verifyOTP = (formValues, actions) => {
return async (dispatch) => {
dispatch(startSubmitting());
const url = `/validate-otp`;
var formdata = new FormData();
formdata.append("mobile", formValues.mobile);
formdata.append("otp", formValues.otp);
const response = await api.post(url, formdata);
dispatch({
type: "VERIFY_OTP",
payload: response,
});
dispatch(stopSubmitting());
await SecureStore.setItemAsync("userToken", response.data.access_token);
};
};
export const checkUser = () => {
return async (dispatch) => {
const token = await SecureStore.getItemAsync("userToken");
const url = `/me`;
const response = await api
.post(url, { token })
.then((res) => {
return res;
})
.catch((error) => {
return error.response;
});
dispatch({
type: "CHECK_USER",
payload: response,
});
};
};
The Problem
you are mixing two different implementations in checkUser to handle a promise which is clearly incorrect and leads to the issues.
The Solution
since your other parts of codes use the async/await so try to remove then/catch block from the response constant:
const checkUser = () => {
return async (dispatch) => {
const url = '/me';
try {
const token = await SecureStore.getItemAsycn("userToken);
const response = await api.post(url, {token})
dispatch({type: "CHECK_USER", payload: response})
} catch (error) {
// to proper action on failure case
}
}
}
Note 1: always use async/await in try/catch block. more on MDN documentation.
Optional
since you are trying to call two async actions (once for getting token and once for calling '/me' API), I encourage you to use two different try/catch blocks to handle the failure case for each async action separately. for example:
const checkUser = () => {
return async (dispatch) => {
let token = null;
try {
token = await SecureStore.getItemAsync("userToken");
} catch (err) {
// proper action in case of failure on getting the token from storage
}
// you may need to ignore API calls without the token, so:
try {
if(token){
const url = '/me';
const response = await api.post(url, {token});
dispatch({type: "CHECK_USER", payload: response});
}
} catch (err) {
// take proper action with the error response according to your applicaiton
}
}
}

Test NestJs API controller with Jest

I am creating an API with NestJs and mysql.
My controller function for create a new entity is working well, however, I can't test the usecase where the response is a 400 error.
This is the controller function :
#Controller('pubs')
export class PubsController {
constructor(private readonly pubsService: PubsService) {}
#Post()
async create(#Body() createPubDto: CreatePubDto, #Res() res: Response): Promise<void> {
this.pubsService.create(createPubDto)
.then(() => res.status(201).json())
.catch(err => res.status(401).json({ err }));
}
}
And this is the test file :
describe('PubsController', () => {
let controller: PubsController;
let service: PubsService;
const mockResponse = () => {
const res: any = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [PubsController],
providers: [PubsService, {
provide: getRepositoryToken(Pub),
useValue: {},
}],
}).compile();
controller = module.get<PubsController>(PubsController);
service = module.get<PubsService>(PubsService);
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
describe('create success', () => {
const res = mockResponse();
it('Should create a pub', async () => {
const req = mockedPub;
jest.spyOn(service, 'create').mockResolvedValue(mockedPub);
await controller.create(req, res);
expect(res.status).toHaveBeenCalledWith(201);
});
it('Should return 400 if the body is not correct', async () => {
const req: any = {};
jest.spyOn(service, 'create').mockResolvedValue(req);
await controller.create(req, res);
expect(res.status).toHaveBeenCalledWith(400);
});
})
});
"Should create a pub" is working well, but when I give to the create function an empty object, the test give me a 201 res.status.
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: 400
Received: 201
Does anybody know why?
First, do you use any validation pipe anywhere in your code in order to validate the incoming CreatePubDto, e.g. in your main.ts file or in the pubService ?
Second, in your test that should fail, you have written jest.spyOn(service, 'create').mockResolvedValue(req); which resolves, and thus you won't catch any error at the controller level, which means you go in the .then(() => res.status(201).json()) of your controller logic.
You should refactor the test to:
it('Should return 400 if the body is not correct', async () => {
const error: any = { message: 'bad DTO provided', code: 400 }; // <== this is where you mock the logic of your service to throw an error
jest.spyOn(service, 'create').mockRejectedValue(error);
await controller.create(req, res);
expect(res.status).toHaveBeenCalledWith(400);
});
This way you're telling Jest to throw an error when the create method is called. I put example error that could be thrown, but feel free to use your own error format that will be thrown.
Also don't forget to align your error code returned in your controller with the one expected in your test. 400 would be the more appropriate I guess in this use case.

How to write business logic in a service as sinon in ExpressJS

I want to use sinon to write test code for the business logic of a service using ExpressJS and Mongoose.
I wrote the following test code, but findOneService takes only id as an argument and returns a document with that id.
//service_test.js
const sinon = require('sinon');
// Service
const { findOneService } = require('../services/service');
// Schema
const Post = require('../models/mongoose/schemas/post');
describe('findOneService', () => {
let find;
beforeEach(() => {
find = sinon.stub(Post, 'findOne');
});
afterEach(() => {
find.restore();
});
it('should findOne', async () => {
const id = ???;
...?
});
})
//service.js
exports.findOneDocument = async (id) => {
const result = await Post.findOne({_id: id});
if (!result) {
throw new Error('404');
}
return result;
};
How can I define the result of this to pass the test code?
To test this kind of behaviour, I strongly suggest an integration test (with an embedded/dockerized MongoDB, for example). This would allow to test-drive more things than just the service, such as schema, migration, db config.
However, if you just want to test-drive the if (!result)... logic, you can stick with sinon. What you're missing is stubbing the return value:
it('returns the document if found', async () => {
find.returns('a post');
expect(await findOneService.findOneDocument('id')).toReturn('a post');
});
it('throw error when document does not exist', async () => {
find.returns(null);
expect(() => await findOneService.findOneDocument('non-existent id')).toThrow(Error);
});