I have a Jasmine test spec test_spec.js like this:
describe('my tests', () => {
it('POST should return 201 created', () => {
var req = {
method: 'POST',
url: '/api/v1.0/message',
payload: JSON.stringify({name: 'Ethan'})
};
server.inject(req, res => {
expect(res.statusCode).to.equal(201);
});
});
});
The route for the API call looks like this:
var routes = [{
path: '/api/v1.0/message',
method: 'POST',
handler: function(request, reply) {
reply('Success').created();
}
}];
exports.register = function(server, options, next) {
server.route(routes);
next();
}
When I run the tests, though, this particular test's expect() function doesn't get called because the server.inject() method doesn't call the response callback. In fact, not even the route handler method gets called (I checked with console.log statements). However, when I change the request method and the route from POST to GET, it works and the test calls the expect() method as expected. The test just doesn't work with POST requests. Am I doing it wrong?
Turns out that the problem was in the test call describe() snippet posted in my question. I neglected to call the done() function inside the server.inject() call. Once I added that, the POST test started getting called:
describe('my tests', () => {
it('POST should return 201 created', (done) => {
var req = {
method: 'POST',
url: '/api/v1.0/message',
payload: JSON.stringify({name: 'Ethan'})
};
server.inject(req, res => {
expect(res.statusCode).toEqual(201);
done();
});
});
});
The need to call the done() callback wasn't obvious to me from the Jasmine documentation. The call is necessary in order to postpone the spec completion until done() is called (meaning payload is posted).
Related
I have a function which returns a middleware as such:
const jsonParser = () => {
return express.json({
limit: '5mb',
verify: (req, res, buf) => {
// If the incoming request is a stripe event,
if (req.headers['some-header']) {
httpContext.set('raw-body', buf.toString());
}
},
});
};
I would like to test that the httpContext.setis indeed called when the some-header header is present.
My test:
describe('jsonParser middleware', () => {
it('sets the http context', async () => {
const req = {
headers: {
'some-header': 'some-sig',
'content-type': 'application/json',
},
body: JSON.stringify({
some: 'thing',
}),
};
const res = {};
const middleware = jsonParser();
middleware(req, res, () => {});
expect(httpContext.set).toHaveBeenCalled();
});
});
I have no idea how to make the test run the function passed to verify. Express docs state that the content type should be json, but nothing more. Anyone that can point me in the right direction is highly appreciated.
Thank you.
as mentioned in the comments i want to give you an example of an integration test which tests the header and jsonwebtoken. i am also using the express framework but i wrote my code in JS.
this is a test for creating a forumpost in a forum i built. a middleware is checking for the token of the user so this case could be similiar to yours.
const request = require('supertest');
test('create authorized 201', async () => {
const forumCountBefore = await ForumPost.countDocuments();
const response = await request(app)
.post('/api/forumPosts')
.set({
Authorization: `Bearer ${forumUserOne.tokens[0].token}`,
userData: {
userId: forumUserOneId,
email: 'forum#controller.com',
username: 'forum',
},
})
.send(forumPost)
.expect(201);
expect(response.body.message).toBe('created forumPost');
const forumCountAfter = await ForumPost.countDocuments();
expect(forumCountBefore + 1).toBe(forumCountAfter);
});
i am using mongoDB thats why i use ForumPost.countDocuments to count the amount of entries in the DB.
as you can see in the test i use supertest (imported as request) to send an http call. in the set block i set the authorization token. this causes the middleware to be executed in the integration test.
the test can only pass when the code of the middleware gets executed correctly so it should cover the code of your middleware.
I got a serverless Netlify function like this:
exports.handler = async function(event, context) {
return {
statusCode: 200,
body: JSON.stringify({message: "Hello World"})
};
}
When called by this url <site-name>/.netlify/functions/helloworld
I do get the message {"message":"Hello World"}
I also got a pages/api/mailingList.js Nextjs API endpoint:
const axios = require('axios');
export default async function handler(req, res) {
//console.log(req.query.mail);
if (req.method === "PUT") {
axios
.put(
"https://api.sendgrid.com/v3/marketing/contacts",
{
contacts: [{ email: `${req.query.mail}` }],
list_ids: [process.env.SENDGRID_MAILING_LIST_ID],
},
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
},
}
)
.then((result) => {
res.status(200).send({
message:
"Your email has been successfully added to the mailing list. Welcome 👋",
});
})
.catch((err) => {
res.status(500).send({
message:
"Oups, there was a problem with your subscription, please try again or contact us",
});
console.error(err);
});
}
}
This mailing list API endpoint, do work when using curl from the terminal with PUT as the method:
curl -X PUT -d mail=helloworld#gmail.com https://netlify.app/api/mailingList
The API endpoint also work from the URL (/api/mailingList?mail=helloworld#gmail.com) when removing the if (req.method === "PUT") { part from the mailingList.js
However, I am NOT able to get the API endpoint to be called from within the Netlify function.
(Preferably the mailingList API should be possible to call multiple times with different mailing list IDs from the Netlify function helloworld.js based on different logic /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
To get the API endpoint to be called at all, from the function, I have tried adding a axios call from the helloworld.js to mailingList.js like this
const axios = require('axios');
exports.handler = async function(event, context) {
const mail = "helloworld#gmail.com";
// add to mailinglist
axios
.put("/api/mailingList?mail="+mail)
.then((result) => {
if (result.status === 200) {
toast.success(result.data.message);
}
})
.catch((err) => {
console.log(err);
});
}
This result in the following error from the browser: error decoding lambda response: invalid status code returned from lambda: 0
(I do not get any error msg from the Netlify log, either helloworld.js or mailingList.js)
Clearly, there is something wrong with how I call the mailigList.js from helloworld.js. Would greatly appreciate if some one could give me some advice and show me what I am doing wrong.
How can I call the API endpoint (mailigList.js) from within the Netlify function helloworld.js? (Preferably multiple times with different mailing list IDs: /api/mailingList?mail=helloworld#gmail.com&listid=xxx)
Found the solution in this article: https://travishorn.com/netlify-lambda-functions-from-scratch-1186f61c659e
const axios = require('axios');
const mail = "helloworld#gmail.com";
exports.handler = (event, context, callback) => {
axios.put("https://<domain>.netlify.app/api/mailingList?mail="+mail)
.then((res) => {
callback(null, {
statusCode: 200,
body: res.data.title,
});
})
.catch((err) => {
callback(err);
});
};
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.
I have almost 13 Axios requests in my Vue application. which are almost the same
axios({
method: 'post',
url: `${this.$root.api_url}/v2/cameras/${this.selected.exid}/nvr/snapshots/extract`,
data: {
start_date: moment(this.fromDateTime).format(),
end_date: moment(this.toDateTime).format(),
schedule: this.schedule,
interval: this.interval,
create_mp4: this.create_mp4,
inject_to_cr: this.inject_to_cr,
jpegs_to_dropbox: this.jpegs_to_dropbox,
requester: this.$root.user.email,
api_key: this.selected.api_key,
api_id: this.selected.api_id
}
}).then(response => {
if (response.status == 201) {
this.showSuccessMsg({
title: "Success",
message: "Snapshot Extractor has been added (Local)!"
});
this.$events.fire('se-added', {})
this.clearForm()
} else {
this.showErrorMsg({
title: "Error",
message: "Something went wrong!"
})
}
})
I pass the method, URL and data.. and do a few things in response and in case of error.
How can I reduce that so much code? I have this idea to make an API file for this where, the method will accept, API.get(method, URL, data) and I will have {message, statusCode} in return. and then on the basis of that, I can do other stu7ff.
I tried to follow some documentation online but it didn't work. Is there any suitable way to reduce this code.
Is it even possible to give success and error message as well in API.get or post or delete that it would be very minimal when you send the API request?
EDIT: so i guess you need something like a class here:
class API {
static get(url, callback) {
axios({
method: "get",
url: url,
data: data
}).then(response => {
callback(response);
});
}
static post(url, data, callback) {
axios({
method: "post",
url: url,
data: data
}).then(response => {
callback(response);
});
}
}
API.post("url", data, response => {
console.log(response);
});
API.get("url", response => {
console.log(response);
});
I use yamlful
You make a .yml file which includes
events:
- method: get
get: /events/:id
then API calls become
const response = await this.$api.events.get(2)
Furthermore, I inject methods into my context
// api.js
async function populateEvents (app, id) {
const response = await app.$api.events.get(id)
return response
}
export default ({ app, store }, inject) => {
inject('populateEvents', id => populateEvents(app, id))
}
// any_file.vue
this.populateEvents(12)
and in api.js you can generalize your api calls, so if any 2 api calls do the same stuff, you can refactor that repeated code into a separate method
I am trying to create a project where a person can create a question and an answer. I am using laravel and Vuex.
I would like to create a variable called Question_id with the response.data after I have called the axios.post to create the question. I would then like to call a function with this Question_id.
I am now noticing though that I cannot do this because when I try to set the question_id variable in .then portion of my axios.post, it happens after I call the other function. In other words, the .then portion happens after all my other code has ran.
qaForm(){
axios
.post("/api/question/create", this.questionForm)
.then(response => {
question = response.data;
question_id = question.id;
})
.catch(error => {
console.log(error.response);
});
addQuestion(question_id);
}
I can confirm this my consoling.out different steps. If I run this experiment:
qaForm(){
console.log("before axios post"); // runs 1st
axios
.post("/api/question/create", this.questionForm)
.then(response => {
console.log("inside axios.then"); // runs 3rd
question = response.data;
question_id = question.id;
})
.catch(error => {
console.log(error.response);
});
console.log("after axios post"); // runs 2nd
addQuestion(question_id);
}
I receive:
before axios post
after axios post
inside axios.then
Why is it this way? Am I making any mistakes? What are some possible workarounds?
Axios requests are asynchronous, and as such return a promise. That is why the code within then() is executed after the code that is below.
the simple fix is to move the code inside the response handler
qaForm(){
console.log("before axios post"); // runs 1st
axios
.post("/api/question/create", this.questionForm)
.then(response => {
console.log("inside axios.then"); // runs 2nd
question = response.data;
question_id = question.id;
console.log("after axios post"); // runs 3rd
addQuestion(question_id);
})
.catch(error => {
console.log(error.response);
});
}
This may seem a little strange when you first see it, it is my preferred way of dealing with asynchronous functions.
The JavaScript language however introduced async-await functionality that would allow you to rewrite the code in a way that you might find more intuitive though.
Note the use of async and await in the code.
async q0aForm() {
console.log("before axios post"); // 1st
let response = await axios.post("/api/question/create", this.questionForm)
console.log("no more axios.then"); // 2nd
question = response.data;
question_id = question.id;
console.log("after axios post"); // 3rd
addQuestion(question_id);
}
I saw your previous code, and everything is working right since is an axios petition. you could try for example in your store something like this.
async actionStore(){
const response = await axios.post('/api/question/create', this.questionForm;
if(isok){
//commit to store
return something;
} else return some error;
}
then in your method inside your component try this.
qaForm()
{
actionStore().then((response)=>{
if(response isok)
addQuestion(question_id);
});
}
It's just an example, cause I can't see all your code right now
remember that actions from store return promises.
If your using '.then' with POST, that my choise.
const blaBla = () => {
const POINT = '{YOUR POINT URL}'
const URL = '{YOUR POST VARIABLES}'
axios({
url: POINT,
data: `producturl=${URL}`,
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then((res) => console.log('res', res.data.content))
}
end worked
blaBla()