How to run test in sequential order in loadimpact? - k6

We have 2 APIs which we wanted to test with load impact and the second API is the so-called dynamic target which is built upon the basis of data we get from the response of the first API.
Hence, We want to run this test sequentially. How can we achieve this?
import { check, sleep } from 'k6';
import http from 'k6/http';
export default function() {
let res, res_body, claim_url
res = http.batch([req])
check(res[0], {
"form data OK": function (res) {
console.log(res.status);
claim_url = JSON.parse(res.body)
console.log(claim_url.details.claim_uri)
return false;
}
});
Does grouping of different APIs in a different function help?

You are not limited to a single http request per default function iteration in any way. So you can just use whatever you want from the previous request and do a new one.
There is an example in the http.post documentation but here is another simple one:
import { check, sleep } from 'k6';
import http from 'k6/http';
export default function() {
let res, res_body, claim_url
res = http.get(req);
check(res, { // check that we actually didn't get error when getting the url
"response code was 200": (res) => res.status == 200,
});
claim_url = JSON.parse(res.body) // if the body is "http://example.org" for example
res2 = http.get(claim_url); // use the returned url
check(res2, { // here it's res2 not res
"response code was 200": (res) => res.status == 200,
});
// do more requests or checks
});

Related

Cypress - Setting global variable from one test to use in other tests

I am trying to set a variable that will contain a part of a URL (a UUID) which I would then like to use in separate test suites. This snippet of the URL will be different every time so I cannot set it in the cypress.json within the "env" options. Code is as follows -
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('/');
const teamsTeamID = arr[4]
cy.log(teamsTeamID)
})
I would then like to use teamsTeamID in a separate teardown test to delete the team at the end of every test run but the team ID will be different every time I run the test - Is there a way to do this?
You can use fixtures and then use readFile and writeFile to achieve this.
First create a json inside your fixtures folder urldata.json
{
"uuid": "17289-YEHBE-893"
}
Then in your test you can write:
var teamsTeamID;
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('/');
teamsTeamID = arr[4]
cy.log(teamsTeamID)
})
cy.readFile("cypress/fixtures/urldata.json", (err, data) => {
if (err) {
return console.error(err);
};
}).then((data) => {
data.uuid = teamsTeamID
cy.writeFile("cypress/fixtures/urldata.json", JSON.stringify(data))
})
So now every time you run the test, you will have different values of UUID in your json file. Next you can use the value from fixtures directly into other tests:
describe('Some page', () => {
beforeEach(function () {
// "this" points at the test context object
cy.fixture('urldata.json').then((urldata) => {
// "this" is still the test context object
this.urldata = urldata
})
})
// the test callback is in "function () { ... }" form
it('check uuid', function () {
// this.urldata exists
expect(this.urldata.uuid).to.equal('some uuid')
})
})
Point to be noted:
If you store and access the fixture data using this test context
object, make sure to use function () { ... } callbacks. Otherwise the
test engine will NOT have this pointing at the test context.

How to test the return value of a promise in a Cucumber test?

I'm trying to test the return value of a get request to a couchdb node.
I have a feature defined with the following Given clause:
Given A get request is made to the DB
which is implemented with the following step function:
var Profile = require('../UserProfile.js')
Given('A get request is made to the DB', function () {
var text = Profile.getDB('localhost:5984').then(data => {
console.log(data)
})
});
The above step references this model:
var axios = require('axios')
module.exports = {
getDB: function(url){
return axios.get(url).then(response => {
return response.data
})
}
};
I can't seem to log the result of the GET request when I perform it in the model and reference it in the step definition. When I do the GET request in the step definition, it works - but this isn't useful to me, I want to test the model. How do I get the resulting value?
Cucumber 2.0 supports Promises as returns, try with:
const Profile = require('../UserProfile')
const { defineSupportCode } = require('cucumber')
defineSupportCode(({ defineStep }) => {
defineStep('A get request is made to the DB', function () {
return Profile.getDB('http://localhost:5984').then(data => {
console.log(data)
})
})
})

How to handle http.get() when received no data in nodejs?

Let's say that I have a service that returns customer information by a given id. If it's not found I return null.
My app calls this services like this:
http.get('http://myserver/myservice/customer/123', function(res) {
res.on('data', function(d) {
callback(null, d);
});
}).on('error', function(e) {
callback(e);
});
Currently my service responds with 200 but no data.
How do I handle when no data is returned?
Should I change it to return a different http code? In this case how to handle this? I tried many different approaches without success.
First off, it's important to remember that res is a stream of data; as it stands, your code is likely to call callback multiple times with chunks of data. (The data event is fired each time new data comes in.) If your method has been working up to this point, it's only because the responses have been small enough that the response payload wasn't broken into multiple chunks.
To make your life easier, use a library that handles buffering HTTP response bodies and allows you to get the complete response. A quick search on npm reveals the request package.
var request = require('request');
request('http://server/customer/123', function (error, response, body) {
if (!error && response.statusCode == 200) {
callback(null, body);
} else {
callback(error || response.statusCode);
}
});
As far as your service goes, if a customer ID is not found, return a 404 – it's semantically invalid to return a 200 OK when in fact there is no such customer.
Use the Client Response 'end' event:
//...
res.on('data', function(d) {
callback(null, d);
}).on('end', function() {
console.log('RESPONSE COMPLETE!');
});
I finally found a way to solve my problem:
http.get('http://myserver/myservice/customer/123', function(res) {
var data = '';
response.on('data', function(d) {
data += d;
});
response.on('end', function() {
if (data != '') {
callback(null, data);
}
else {
callback('No customer was found');
}
});
}).on('error', function(e) {
callback(e);
});
This solves the problem, but I'm also going to adopt josh3736's suggestion and return a 404 on my service when the customer is not found.

Get the whole response body when the response is chunked?

I'm making a HTTP request and listen for "data":
response.on("data", function (data) { ... })
The problem is that the response is chunked so the "data" is just a piece of the body sent back.
How do I get the whole body sent back?
request.on('response', function (response) {
var body = '';
response.on('data', function (chunk) {
body += chunk;
});
response.on('end', function () {
console.log('BODY: ' + body);
});
});
request.end();
Over at https://groups.google.com/forum/?fromgroups=#!topic/nodejs/75gfvfg6xuc, Tane Piper provides a good solution very similar to scriptfromscratch's, but for the case of a JSON response:
request.on('response',function(response){
var data = [];
response.on('data', function(chunk) {
data.push(chunk);
});
response.on('end', function() {
var result = JSON.parse(data.join(''))
return result
});
});`
This addresses the issue that OP brought up in the comments section of scriptfromscratch's answer.
I never worked with the HTTP-Client library, but since it works just like the server API, try something like this:
var data = '';
response.on('data', function(chunk) {
// append chunk to your data
data += chunk;
});
response.on('end', function() {
// work with your data var
});
See node.js docs for reference.
In order to support the full spectrum of possible HTTP applications, Node.js's HTTP API is very low-level. So data is received chunk by chunk not as whole.
There are two approaches you can take to this problem:
1) Collect data across multiple "data" events and append the results
together prior to printing the output. Use the "end" event to determine
when the stream is finished and you can write the output.
var http = require('http') ;
http.get('some/url' , function (resp) {
var respContent = '' ;
resp.on('data' , function (data) {
respContent += data.toString() ;//data is a buffer instance
}) ;
resp.on('end' , function() {
console.log(respContent) ;
}) ;
}).on('error' , console.error) ;
2) Use a third-party package to abstract the difficulties involved in
collecting an entire stream of data. Two different packages provide a
useful API for solving this problem (there are likely more!): bl (Buffer
List) and concat-stream; take your pick!
var http = require('http') ;
var bl = require('bl') ;
http.get('some/url', function (response) {
response.pipe(bl(function (err, data) {
if (err) {
return console.error(err)
}
data = data.toString() ;
console.log(data) ;
}))
}).on('error' , console.error) ;
The reason it's messed up is because you need to call JSON.parse(data.toString()). Data is a buffer so you can't just parse it directly.
If you don't mind using the request library
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Print the google web page.
}
})
If you are dealing with non-ASCII contents(Especially for Chinese/Japanese/Korean characters, no matter what encoding they are), you'd better not treat chunk data passed over response.on('data') event as string directly.
Concatenate them as byte buffers and decode them in response.on('end') only to get the correct result.
// Snippet in TypeScript syntax:
//
// Assuming that the server-side will accept the "test_string" you post, and
// respond a string that concatenates the content of "test_string" for many
// times so that it will triggers multiple times of the on("data") events.
//
const data2Post = '{"test_string": "swamps/沼泽/沼澤/沼地/늪"}';
const postOptions = {
hostname: "localhost",
port: 5000,
path: "/testService",
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data2Post) // Do not use data2Post.length on CJK string, it will return improper value for 'Content-Length'
},
timeout: 5000
};
let body: string = '';
let body_chunks: Array<Buffer> = [];
let body_chunks_bytelength: number = 0; // Used to terminate connection of too large POST response if you need.
let postReq = http.request(postOptions, (res) => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', (chunk: Buffer) => {
body_chunks.push(chunk);
body_chunks_bytelength += chunk.byteLength;
// Debug print. Please note that as the chunk may contain incomplete characters, the decoding may not be correct here. Only used to demonstrating the difference compare to the final result in the res.on("end") event.
console.log("Partial body: " + chunk.toString("utf8"));
// Terminate the connection in case the POST response is too large. (10*1024*1024 = 10MB)
if (body_chunks_bytelength > 10*1024*1024) {
postReq.connection.destroy();
console.error("Too large POST response. Connection terminated.");
}
});
res.on('end', () => {
// Decoding the correctly concatenated response data
let mergedBodyChunkBuffer:Buffer = Buffer.concat(body_chunks);
body = mergedBodyChunkBuffer.toString("utf8");
console.log("Body using chunk: " + body);
console.log(`body_chunks_bytelength=${body_chunks_bytelength}`);
});
});
How about HTTPS chunked response? I've been trying to read a response from an API that response over HTTPS with a header Transfer-Encoding: chunked. Each chunk is a Buffer but when I concat them all together and try converting to string with UTF-8 I get weird characters.

React-redux project - chained dependent async calls not working with redux-promise middleware?

I'm new to using redux, and I'm trying to set up redux-promise as middleware. I have this case I can't seem to get to work (things work for me when I'm just trying to do one async call without chaining)
Say I have two API calls:
1) getItem(someId) -> {attr1: something, attr2: something, tagIds: [...]}
2) getTags() -> [{someTagObject1}, {someTagObject2}]
I need to call the first one, and get an item, then get all the tags, and then return an object that contains both the item and the tags relating to that item.
Right now, my action creator is like this:
export function fetchTagsForItem(id = null, params = new Map()) {
return {
type: FETCH_ITEM_INFO,
payload: getItem(...) // some axios call
.then(item => getTags() // gets all tags
.then(tags => toItemDetails(tags.data, item.data)))
}
}
I have a console.log in toItemDetails, and I can see that when the calls are completed, we eventually get into toItemDetails and result in the right information. However, it looks like we're getting to the reducer before the calls are completed, and I'm just getting an undefined payload from the reducer (and it doesn't try again). The reducer is just trying to return action.payload for this case.
I know the chained calls aren't great, but I'd at least like to see it working. Is this something that can be done with just redux-promise? If not, any examples of how to get this functioning would be greatly appreciated!
I filled in your missing code with placeholder functions and it worked for me - my payload ended up containing a promise which resolved to the return value of toItemDetails. So maybe it's something in the code you haven't included here.
function getItem(id) {
return Promise.resolve({
attr1: 'hello',
data: 'data inside item',
tagIds: [1, 3, 5]
});
}
function getTags(tagIds) {
return Promise.resolve({ data: 'abc' });
}
function toItemDetails(tagData, itemData) {
return { itemDetails: { tagData, itemData } };
}
function fetchTagsForItem(id = null) {
let itemFromAxios;
return {
type: 'FETCH_ITEM_INFO',
payload: getItem(id)
.then(item => {
itemFromAxios = item;
return getTags(item.tagIds);
})
.then(tags => toItemDetails(tags.data, itemFromAxios.data))
};
}
const action = fetchTagsForItem(1);
action.payload.then(result => {
console.log(`result: ${JSON.stringify(result)}`);
});
Output:
result: {"itemDetails":{"tagData":"abc","itemData":"data inside item"}}
In order to access item in the second step, you'll need to store it in a variable that is declared in the function scope of fetchTagsForItem, because the two .thens are essentially siblings: both can access the enclosing scope, but the second call to .then won't have access to vars declared in the first one.
Separation of concerns
The code that creates the action you send to Redux is also making multiple Axios calls and massaging the returned data. This makes it more complicated to read and understand, and will make it harder to do things like handle errors in your Axios calls. I suggest splitting things up. One option:
Put any code that calls Axios in its own function
Set payload to the return value of that function.
Move that function, and all other funcs that call Axios, into a separate file (or set of files). That file becomes your API client.
This would look something like:
// apiclient.js
const BASE_URL = 'https://yourapiserver.com/';
const makeUrl = (relativeUrl) => BASE_URL + relativeUrl;
function getItemById(id) {
return axios.get(makeUrl(GET_ITEM_URL) + id);
}
function fetchTagsForItemWithId(id) {
...
}
// Other client calls and helper funcs here
export default {
fetchTagsForItemWithId
};
Your actions file:
// items-actions.js
import ApiClient from './api-client';
function fetchItemTags(id) {
const itemInfoPromise = ApiClient.fetchTagsForItemWithId(id);
return {
type: 'FETCH_ITEM_INFO',
payload: itemInfoPromise
};
}

Resources