I've been trying to solve this issue for days;
create the test for this case using mocha:
app.post('/approval', function(req, response){
request.post('https://git.ecommchannel.com/api/v4/users/' + req.body.content.id + '/' + req.body.content.state + '?private_token=blabla', function (error, resp, body) {
if (resp.statusCode == 201) {
//do something
} else {
response.send("failed"), response.end();
}
});
} else {
response.send("failed"), response.end();
}
});
});
I've tried several ways, using supertest to test the '/approval' and using nock to test the post request to git api. But it always turn "statusCode" is undefined. I think that's because the request to git api in index.js is not inside a certain function(?)
So I can't implement something like this :
https://codeburst.io/testing-mocking-http-requests-with-nock-480e3f164851 or
https://scotch.io/tutorials/nodejs-tests-mocking-http-requests
const nockingGit = () => {
nock('https://git.ecommchannel.com/api/v4/users')
.post('/1/yes', 'private_token=blabla')
.reply(201, { "statusCode": 201 });
};
it('approval', (done) => {
let req = {
content: {
id: 1,
state: 'yes'
},
_id: 1
}
request(_import.app)
.post('/approval')
.send(req)
.expect(200)
.expect('Content-Type', /html/)
.end(function (err, res) {
if (!err) {
nockingGit();
} else {
done(err);
}
});
done();
})
Then I tried to use supertest as promise
it('approve-block-using-promise', () => {
return promise(_import.app)
.post('/approval')
.send(req = {
content: {
id: 1,
state: 'yes'
},
_id: 1
})
.expect(200)
.then(function(res){
return promise(_import.app)
.post("https://git.ecommchannel.com/api/v4/users/")
.send('1/yes', 'private_token=blabla')
.expect(201);
})
})
But it gives error: ECONNEREFUSED: Connection refused. I didn't find any solution to solve the error. Some sources said that it needs done() .. but it gives another error message, 'ensure "done()" is called" >.<
So then I've found another way, using async (https://code-examples.net/en/q/141ce32)
it('should respond to only certain methods', function(done) {
async.series([
function(cb) { request(_import.app).post('/approval')
.send(req = {
content: {
id: 1,
state: 'yes'
},
_id: 1
})
.expect(200, cb); },
function(cb) { request(_import.app).post('/https://git.ecommchannel.com/api/v4/users/').send('1/yes', 'private_token=blabla').expect(201, cb); },
], done);
});
and it gives this error : expected 201 "Created", got 404 "Not Found". Well, if I open https://git.ecommchannel.com/api/v4/users/1/yes?private_token=blabla in the browser it does return 404. But what I expect is I've injected the response to 201 from the unit test; so whatever the actual response is, the statusCode suppose to be 201, right?
But then since it gives that error, is it means the unit test really send the request to the api?
Pls help me to solve this; how to test the first code I shared.
I really new into unit test.
There are a few things wrong with your posted code, I'll try to list them out but I'm also including a full, passing example below.
First off, your call to git.ecommchannel in the controller, it's a POST with no body. While this isn't causing the errors you're seeing and is technically not incorrect, it is odd. So you should double check what the data you should be sending is.
Next, I'm assuming this was a copy/paste issue when you created the question, but the callback for the request in your controller is not valid JS. The brackets don't match up and the send "failed" is there twice.
Your Nock setup had two issues. First the argument to nock should only have origin, none of the path. So /api/v4/users had to be moved into the first argument of the post method. The other issue was with the second argument passed to post that is an optional match of the POST body. As stated above, you aren't currently sending a body so Nock will always fail to match and replace that request. In the example below, the private_token has been moved to match against the query string of the request, as that what was shown as happening.
The calling of nockingGit was happening too late. Nock needs to register the mock before you use Supertest to call your Express app. You have it being called in the end method, by that time it's too late.
The test labeled approve-block-using-promise has an issue with the second call to the app. It's calling post via Supertest on the Express app, however, the first argument to that post method is the path of the request you're making to your app. It has nothing to do with the call to git.ecommchannel. So in that case your Express app should have returned a 404 Not Found.
const express = require('express')
const nock = require('nock')
const request = require('request')
const supertest = require('supertest')
const app = express()
app.use(express.json())
app.post('/approval', function(req, response) {
const url = 'https://git.ecommchannel.com/api/v4/users/' + req.body.content.id + '/' + req.body.content.state
request.post({
url,
qs: {private_token: 'blabla'}
// body: {} // no body?
},
function(error, resp, body) {
if (error) {
response.status(500).json({message: error.message})
} else if (resp.statusCode === 201) {
response.status(200).send("OK")
} else {
response.status(500).send("failed").end();
}
});
});
const nockingGit = () => {
nock('https://git.ecommchannel.com')
.post('/api/v4/users/1/yes')
.query({private_token: 'blabla'})
.reply(201, {"data": "hello world"});
};
it('approval', (done) => {
const reqPayload = {
content: {
id: 1,
state: 'yes'
},
_id: 1
}
nockingGit();
supertest(app)
.post('/approval')
.send(reqPayload)
.expect(200)
.expect('Content-Type', /html/)
.end(function(err) {
done(err);
})
})
Related
Until recently (one of the last full .net SF versions), I could call a Serenity service endpoint like below and react on success or failure. With current .net core (3.14.3) SF, somehow this seems not anymore to work.
I just get a dialog with the message content. I neither get "success" nor "error" alert box.
Question: How to do this with current SF 3.14.3.
Here my code from a project on full .net where this still works:
let bla1 = CountriesService.ImportCountriesFromRESTCountries(
{
},
response => {
alert('success');
let message = JSON.parse(bla1.responseText);
Q.notifySuccess(message, Q.text("Dialogs.Button.UpdateCountries.Import.Toast.Title"), options);
this.refresh();
},
{
blockUI: true,
onError: response => {
alert('error');
let errorcontent = JSON.parse(bla1.responseText);
let message = errorcontent["Error"]["Message"]
Q.alert(message);
this.refresh();
}
});
face same issue , i resolved this by
Q.serviceCall<Serenity.RetrieveResponse<any>>({
service: this.serviceUrl + '/Retrieve',
request: {
EntityId: this.value
} as Serenity.RetrieveRequest,
async: false,
onSuccess: (response) => {
this._selectedItem = response.Entity;
},
onError: (error) => {
console.log( error.Error);
}
});
I'm pretty new to sinon and proxyquire and I think I've read all the answers here on SO but I'm still not finding out what I need. Anyway, here's a sanitized version of my code.
const fetch = require('node-fetch');
async function deleteID(id, endpoint) {
try {
let url = `${endpoint}/delete/${id}`;
let res = await fetch(url, { method: 'DELETE' });
res = await res.json(); // <---- THIS FAILS WHEN fetch IS MOCKED
// do stuff with res
} catch (err) {
logger.error(`Error: ${JSON.stringify(err)}`);
}
}
It's pretty simple, it uses node-fetch to hit a url and then does stuff if the request succeeds or fails. Here's my test, lets setup the mocking for fetch:
const proxyquire = require('proxyquire').noCallThru();
const sinon = require('sinon');
beforeEach((done) => {
const validResponse = {
status: 200,
data: 'hello, world\n'
};
deleteProxy = proxyquire('./delete', {
'node-fetch': sinon.stub().returns(Promise.resolve(JSON.stringify(validResponse)))
});
});
So the fetch call now returns validResponse instead of hitting the server. And here's my test:
it.only('should delete', async () => {
try {
deleteProxy.deleteID('id', 'endpoint');
} catch (err) {
expect(err.message).to.have.lengthOf.at.least(0);
}
});
This fails since res is just an object with status and data, it is not a proper Response that has a Body etc... The rest of our code uses node-mocks-http but all of the tests using that module hit the url directly, not indirectly, via fetch, like I'm doing above.
How do I either create a mocked Response to fit into the above test or is there a different approach I should be using?
By looking at the code and my experience with sinon I would say as this is not an actual HTTP response so you have to mock json() as well.
In beforeEach method:
const body = {
status: 200,
data: 'hello, world\n'
};
var validResponse = { json: () => { return body } };
deleteProxy = proxyquire('./delete', {
'node-fetch': sinon.stub().returns(Promise.resolve(validResponse))
});
try with out JSON.stringify()
Let me know if it doesn't work.
I got two requests: A and B in postman. I want to send the request A first then send the request B while request A is still waiting for the response. It's quite easy to do this mannually because request A take 15s to finish.
But are there anyway I can do this automatically, because I am going to test this case a lot.
I have tried to use runner in postman but it always wait for request A to be finish before sending request B.
After that I found a document about sending asynchronous request in postman here.
I wrote a script that use pm.sendRequest to send request B and put that script in the pre-request of request A.
let confirmRequest = {
url: url + "/confirm",
method: "POST",
body: {
"requestId": (new Date()).getTime(),
"statusCode": "0",
}
}
setTimeout(function() {
pm.sendRequest(confirmRequest, function (err, res) {
console.log(err ? err : res.json());
});
}, 1500);
the problem is even I've wrapped it inside a setTimeout function, the request A still wait for pre-request to finish first. So in the end request B have been send before request A.
Are there any solution to this problem?
I tried but could not achieve asynchronously process requests using Postman or Newman. I found it easier to write a nodeJS code using async-await-promise concepts. Here is the sample code:
Sample Code that works for me:
var httpRequest "your raw request body";
var headersOpt = {
"content-type": "application/json",
};
const promisifiedRequest = function(options) {
return new Promise((resolve,reject) => {
request(options, (error, response, body) => {
if (response) {
return resolve(response);
}
if (error) {
return reject(error);
}
});
});
};
var output;
async function MyRequest(httpRequest,j, fileName) {
var options = {
uri: "url",
method: "POST",
body: httpRequest,
json: true,
time: true,
headers: headersOpt
}
try {
console.log('request posted!');
let response = await promisifiedRequest(options);
console.log('response recieved!');
output = output + ',' +response.elapsedTime;
console.log(response.elapsedTime);
console.log(output);
//return response;
} catch (err) {
console.log(err);
}
finally
{
//this code is optional and it used to save the response time for each request.
try{
fileName = (j+1)+'_'+fileName;
fs.writeFile('/logs-async/scripts/output/'+fileName+'.csv', output, (err) => {
//throws an error, you could also catch it here
if (err) throw err;
});
}
catch (err){
console.log(err);
}
}
}
I am not being able to get the response body that I need to store in the logs along with the request body. The only time I'm able to get the response body is when the request fails.
I've followed the blog post that solved my issue on getting the request body while using body-parser plugin - https://www.express-gateway.io/exploit-request-stream/.
const { PassThrough } = require("stream");
const jsonParser = require("express").json();
const urlEncodedParser = require("express").urlencoded({ extended: true });
module.exports = {
name: 'body-parser',
policy: actionParams => {
return (req, res, next) => {
req.egContext.requestStream = new PassThrough()
req.pipe(req.egContext.requestStream)
return jsonParser(req, res, () => urlEncodedParser(req, res, next))
}
}
};
When the request does work:
{ res: { statusCode: 400 },
req:
{ body: { a: 'b' },
headers:
{ ... } },
responseTime: 310 }
When it does not work:
{ res: { body: 'Bad gateway.', statusCode: 502 },
req:
{ body: { a: 'b' },
headers:
{ ... } },
responseTime: 1019 }
this code alone is not enough to get the response body. This will simply hook in the request body processing and make it available to EG in a parsed way. In case you want to hook in the response too, you will need to write an hook in the response object, once it's done.
You can find an example code here
I hope that helps!
V.
I'm trying to unit test my http.get/post/etc responses.
I found this tutorial that was extremely helpful: https://medium.com/spektrakel-blog/angular-testing-snippets-httpclient-d1dc2f035eb8
Going through and following that, I've configured my unit tests and I'm able to get everything working, but there's one part that I have that is inconsistent with the tutorial...
In the tutorial, it shows to test the service login function like this:
it(`should emit 'true' for 200 Ok`, async(inject([HttpClientFeatureService, HttpTestingController],
(service: HttpClientFeatureService, backend: HttpTestingController) => {
service.login('foo', 'bar').subscribe((next) => {
expect(next).toBeTruthy();
});
backend.expectOne('auth/login').flush(null, { status: 200, statusText: 'Ok' });
})));
And here's the actual method on the service that is being tested:
login(user: string, password: string): Observable<boolean> {
const body = new HttpParams()
.set(`user`, user)
.set(`password`, password);
const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });
return this.http.post(`auth/login`, body.toString(), { headers, observe: 'response' })
.map((res: HttpResponse<Object>) => res.ok)
.catch((err: any) => Observable.of(false));
}
Here's my login function:
login(username: string, password: string): Observable<any> {
this.loggingService.log('LoginService | login | username: ' + username + '; password: xxxxx');
return this.http.post(this.loginUrl, { username: username, password: password })
.map((response: any) => {
console.log('response: ' + JSON.stringify(response));
if (response && response.length > 0) {
return response;
} else {
return this.parseErrorResponse(response);
}
});
}
And here's my unit test:
it('login should return a valid JWT', async(inject([LoginService, HttpTestingController], (service: LoginService, backend: HttpTestingController) => {
service.login('user', 'password').subscribe((next) => {
expect(next).toEqual('asdfasdfasdf');
});
backend.expectOne(environment.authenticationServiceBaseUrl + 'api/login')
.flush('asdfasdfasdf', { status: 200, statusText: 'Ok' });
})));
You'll notice the difference here is in the map response section. My version is getting back just a string from the unit test's http.post call, while the example shows that it's returning an HttpResponse object and is just checking that the statusText property is equal to 'Ok'.
Why is my version returning just the string, while the examples version is returning the actual HttpResponse (which includes status and statusText)? I WANT the tutorial version here...
The example shows that it returns null in the body of the response via the flush function call, while I had to add my dummy JWT value in there in order to get my test to pass. Even when I specify that as null to be like the test, then the response that I get in the unit test is null.
Where am I going wrong here?
The tutorial uses observe: 'response', which means that events emitted by the returned observable are responses, and not just the body.
This is covered in the http guide.