I am creating an API application in NodeJS using the Serverless framework. I have installed the serverless-mocha-plugin and am trying to create some unit tests for my functions.
In my serverless.yml file, I have the following endpoints:
...
equipmentGetAll:
handler: ./api/equipment/equipment.getAll
events:
- http:
path: equipment
method: get
cors: true
equipmentGetOne:
handler: ./api/equipment/equipment.getOne
events:
- http:
path: equipment/{po_number}
method: get
cors: true
...
When testing the getAll endpoint, I use the following test which passes successfully. I have verified it works by logging the response to the console.
'use strict';
// tests for equipmentGetAll
// Generated by serverless-mocha-plugin
const mochaPlugin = require('serverless-mocha-plugin');
const expect = mochaPlugin.chai.expect;
let wrapped = mochaPlugin.getWrapper('equipmentGetAll', '/api/equipment/equipment.js', 'getAll');
describe('equipmentGetAll', () => {
before((done) => {
done();
});
it('should get all Equipment', () => {
return wrapped.run({po_number:117}).then((response) => {
expect(response.statusCode).to.be.equal(200);
expect(response.body.length).to.be.greaterThan(0);
});
});
});
Similarly, for the getOneendpoint, I am (for now) doing a very similar test:
'use strict';
// tests for equipmentGetOne
// Generated by serverless-mocha-plugin
const mochaPlugin = require('serverless-mocha-plugin');
const expect = mochaPlugin.chai.expect;
let wrapped = mochaPlugin.getWrapper('equipmentGetOne', '/api/equipment/equipment.js', 'getOne');
describe('equipmentGetOne', () => {
before((done) => {
done();
});
it('should get one single Equipment', () => {
return wrapped.run({}).then((response) => {
expect(response.statusCode).to.be.equal(200);
expect(response.body.length).to.be.equal(1);
});
});
});
The Problem
The current response I'm receiving for getOne is:
{
statusCode: 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Cannot read property \'po_number\' of undefined'
}
Due to the fact that the path for getOne from serverless.yml is equipment/{po_number} rather than just equipment/.
What is the proper way to pass the path value for the test?
A sample call would hit endpoint my-api-endpoint.com/equipment/117 and return the Equipment with po_number 117. This works properly when testing with POSTMan, but how can I make it work with mocha-serverless-plugin?
To pass data to lambda you should use
wrappedLambda.run({body: "String, not Object"})
To pass queryStringParametr to lambda you should use wrappedLambda.run({queryStringParameters: {a: "first",b:"second"}})
To pass pathParameters to lambda you should use
wrappedLambda.run({pathParameters: {a: "first", b:"second"})
Simple example for testing post method
context('save flashcard', () => {
before((done) => {
done();
});
it('save flashcard successfully', () => {
return saveFlashcard.run({body: correctInput})
.then((response) => {
const body = JSON.parse(response.body);
expect(body).to.have.property('_id')
})
});
});
this body will be located inside event object.
To pass body you need to do something like this
{
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
releaseDate: 2231213213,
title: 'sfsdf',
authorName: 'L'
})
}
Related
I have a service consumer pact test that I am writing, and it seems that when an API call is made it will remove the base url from the request path.
For context, here is the test I am attempting to run.
import { pactWith } from 'jest-pact';
import { Matchers } from '#pact-foundation/pact';
import { ProviderApi } from 'provider-app-api';
import fetch from 'node-fetch';
globalThis.fetch = fetch;
pactWith(
{ consumer: 'ConsumerApp', provider: 'ProviderApp', port: 1234 },
(provider) => {
let providerApi;
beforeEach(() => {
providerApi = new ProviderApi(
provider.mockService.baseUrl,
'access_token'
);
});
describe('ProviderApp API', () => {
beforeEach(() => {
return provider.addInteraction({
state: 'A get request to /segments/{segment_code}/makes',
uponReceiving: 'Some makes exist with segment code vehicles',
withRequest: {
method: 'GET',
path: `${provider.mockService.baseUrl}/segments/vehicles/makes`,
headers: { Authorization: 'Bearer access_token' },
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json; charset=utf8' },
body: Matchers.like({
id: 1,
code: 'TOYO',
description: 'Toyota',
start_year: 2011,
end_year: 2021,
segment_code: 'vehicles',
}),
},
});
});
it('returns a successful body', () => {
return vehiclelinkApi.fetchMakes('vehicles').then((response) => {
// assertions to go here
expect(true).toBeTruthy();
});
});
});
}
);
Upon running the test, I get this output:
$ yarn run test:consumer_pact
console.error
at node_modules/#pact-foundation/src/httpPact.ts:121:17
console.error
Pact verification failed!
at node_modules/#pact-foundation/src/httpPact.ts:122:17
console.error
Actual interactions do not match expected interactions for mock MockService.
Missing requests:
GET http://127.0.0.1:1234/segments/vehicles/makes
Unexpected requests:
GET /segments/vehicles/makes
See /home/stefan/project/pact/logs/ConsumerApp-ProviderApp-mockserver-interaction-port-1234.log for details.
It would seem that it's remving the base URL from the fetch call, so the pact server never receives the request, which makes sense. How do I force this to be appended in the call when I use the ProviderAPI? I've ensured that I'm passing provider.mockService.baseUrl in the request, and I've ensured that the value is localhost:1234. Is this an issue that would need to be resolved inside of the ProviderApi package?
That baseUrl shouldn't be in the path property, which should just take the path, not the full URI
I.e. it should just be this
path: "/segments/vehicles/makes",
I've got an existing working test that is testing a route in an Express app (trimmed code):
const AWS = require('aws-sdk-mock');
const AWS_SDK = require('aws-sdk');
AWS.setSDKInstance(AWS_SDK);
...
before(() => {
sendEmailMock = sinon.stub().callsArgWith(1, null, 'All is well');
AWS.mock('SES', 'sendEmail', sendEmailMock);
server = rewire('../../../..');
...
describe('POST:/feedback', () => {
it('Returns 200 with a fully formed request', (done) => {
request(app)
.post('/gethelp/api/v1/feedback')
.send({
thumbsUp: 'true',
title: 'Abcdef ghi',
url: 'http://google.com',
comments: 'lkajsdj lkajsdkjf aslkjdfa asjdflasjd lkfj',
})
.expect(200, () => {
const args = sendEmailMock.args[0][0];
... etc
This is a working test. But I need to refactor it to not use the full server (because it's doing some integration stuff on startup). So I'm bringing in node-mocks-http:
const httpMocks = require('node-mocks-http');
const feedbackRouteHandler = require('./feedback');
...
before(() => {
sendEmailMock = sinon.stub().callsArgWith(1, null, 'All is well');
AWS.mock('SES', 'sendEmail', sendEmailMock);
});
...
const mockRequest = httpMocks.createRequest({
method: 'POST',
url: '/gethelp/api/v1/feedback',
body: {
thumbsUp: 'true',
title: 'Abcdef ghi',
url: 'http://google.com',
comments: 'lkajsdj lkajsdkjf aslkjdfa asjdflasjd lkfj',
},
});
const mockResponse = httpMocks.createResponse();
feedbackRouteHandler(mockRequest, mockResponse);
expect(mockResponse.statusCode).to.equal(200);
expect(sendEmailMock.args).to.exist;
The problem is that adding in node-mocks-http appears to have broken the mocking of the AWS SDK. When sendEmail is hit it's hitting the actual AWS SDK, not the mocked version. It was hitting the mocked version in the previous version of the test.
How can I use node-mocks-http with aws-sdk-mock?
I cant execute a second request inside the request callback.
request({
url: url,
headers: {
'auth-token': token
},
method: 'POST',
json: true,
body: data
}, (err, req, body) => {
if (err) {
console.log(err);
} else {
// Prosses data;
// This is the second request.
request({
url: url2,
headers; {
'auth-token': token
},
method: 'POST',
json: true,
body: data2
}, (err, req, body) => {
if (err) {
console.log(err);
return;
}
//Process data.
})
}
})
The problem is that the second request is not executing.
I am using nodemon to start the express server, but on the nodemon only the first request is receive on the express.
But the strange thing is that when I tried to call the method on the second time without closing the electron app, the second request is executed. And I can see it on the nodemon that the second request is executed first.
The output of the nodemon is like this.
POST /path/to/url 200 6.181 ms - 641 //-> this is the first execution. then there is nothing follows.
// Then I call the method again using the app. And the result is this.
POST /path/to/second/url 200 9.645 ms - 21
POST /path/to/url 200 21.628 - 641
It look like the /path/to/second/url is staying on somewhere nowhere and just send to the server if the method is called for the second time.
Please help, thanks.
Update: Added more info.
I have a folder could routes all the .js file is save there.
then I am loading it using this on the my app.js
let rs = fs.readdirSync(path.join(process.cwd(), '/routes/'));
rs.forEach((file) => {
if (file.indexOf('.js') !== -1) {
let fn = '/' + file.replace(/\.[^/.]+$/, '');
let pt = path.join(__dirname, './routes', fn);
let req = require(pt);
app.use(fn, req);
}
});
Then on the routes files I have something similar like this.
router.post('url', (req, res) => {
// here calling the controller.
// mostly just single line passing the request and result variable to the controller.
});
Actually that requests is called inside the ipc callback. I have a menuitems and on the click() event I just used the browserWindow.getFocusedWindow().webContents.send('ipc-name') then this will be triggered.
ipc.on('ipc-name', () => {
// The request is called here.
}
This does not solve the OP problem as the problem exists in Linux env but acts as an workaround.Instead of request module we have to make use of ClientRequest API in electron which is Event based and only makes the task much more difficult.But doesn't suffer from the issue faced in callback inside callback.
Example Code:
function triggerCall() {
const request = net.request(`${root}/routes/get1`);
request.on('response', (response) => {
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`)
})
response.on('end', () => {
console.log('req 1 end');
const request1 = net.request(`${root}/routes/get2`);
request1.on('response', (response) => {
response.on('data', (chunk) => {
console.log(`BODY: ${chunk}`)
})
response.on('end', () => {
console.log('req 2 end');
})
})
request1.end();
})
})
request.end();
};
I wanted to pass the property value I used in my HTTP POST request so I can use it in another test.
const device = `28` + (new Date().getTime())
describe('register device', () => {
test('create device registration', async () => {
try {
const registerDevice= await axios.post(`${baseUrl}register`, {
deviceId: (`${deviceId}`),
platformId: 'ios',
osVersion: '11.0'
}, {
timeout,
headers: {
'Authorization': `Bearer ${tokenGenerator.generateAuthToken({
name: 'namehere'
})}`
}
})
expect(registerDevice.status).toBe(200)
})
})
I wanted to pass the same value I used for the deviceId from the POST request above so I can put it on the payload for my next POST request. Any suggestions will be helpful. Thanks!
I just started building a small app using node and redux by adding on to react-redux-starter-kit.
I am now trying to test an async action which performs an API call, while adhering closely to the redux example.
I am using the package isomorphic-fetch to perform the request and fetch-mock to mock it, but when I run my tests, it performs a real request to my API.
I already noticed that fetch-mock works as expected when I perform the API call right in my it-block, but actually I want to test an imported function that performs the API call.
What do I need to do to get it to work also for an imported function?
This is what my action looks like:
require('es6-promise').polyfill()
const fetch = require('isomorphic-fetch')
export const authenticateUserCredentials = ({email, password}) => {
return (dispatch) => {
return fetch('http://localhost:3005/v1/sign_in', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
}).then(response => response.json())
.then(json => {
console.log('json', json)
})
.catch((reason) => {
console.log('CATCHED ERROR:', reason.name, reason.message)
})
}
}
export const actions = {
authenticateUserCredentials
}
And in my spec file:
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import { actions } from 'redux/modules/session'
const fetchMock = require('fetch-mock')
const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)
describe('(Async Action Creator) Authenticate user credentials', function () {
const EMAIL = 'a#b.de'
const TOKEN = '893irnjkklfnt'
const PASSWORD = 'foobar'
beforeEach(function () {
fetchMock.mock(
'http://localhost:3005/v1/sign_in',
'POST', {
status: 200,
body: '{"email":"' + EMAIL + '","token":"' + TOKEN + '"}'
}
)
})
afterEach(function () {
fetchMock.restore()
})
it('creates SIGN_IN when credentials are valid', (done) => {
const initialState = {}
const expectedActions = [
{ type: AUTHENTICATING, payload: undefined },
{ type: SIGN_IN, payload: {email: EMAIL, token: TOKEN} }
]
const store = mockStore(initialState, expectedActions, () => {
return done()
})
store.dispatch(
actions.authenticateUserCredentials({
email: EMAIL,
password: PASSWORD
})
)
})
})
A better solution is to not assign isomorphic-fetch to a constant as it will already assign itself as a global variable. fetch-mock is designed to work with fetch as a global as that's what the standard says fetch should be. It is possible to get it to work with fetch assigned to some other variable, but it means jumping through unnecessary hoops.
Using this package solves the problem: https://github.com/spaceviewinc/fetch-mock-forwarder.