I'm using jMeter 3.2 to write some tests. I have a CSV file with test account info. Each row contains login info for a user. Each user needs to request a token that is used on later requests.
My test plan:
The get token request retrieves a token. The login requests logs in the user and returns another token. Select customer card selects a customer and returns the final token. The code for the postprocesser is (I'm not experienced in this, so any advice is appreciated):
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
// Check if our map already exists
if (props.get("map") == null) {
JSONObject obj = new JSONObject();
obj.put("${department}", new String(data));
log.info("Adding department to map. Department: ${department}. Token: " + new String(data));
props.put("map", obj.toJSONString());
} else {
// Retrieve the current map
map = props.get("map");
JSONParser parser = new JSONParser();
JSONObject jobj = (JSONObject) parser.parse(map);
// Add the new department (with it's token) to the map
jobj.put("${department}", new String(data));
log.info("Updating map for department. Department: ${department}. Token: " + new String(data));
props.put("map", jobj.toJSONString());
}
Attempt 1:
I'm setting up a once only controller to log in a user and retrieve the token.
Now lets say I have 10 lines in my CSV file but in my test I only want to use 3 users and loop 10 times. What happens is that 3 login requests are sent (one for each user). This works fine for the first iteration. At the 2. iteration the 3 threads will use row 4-6 which doesn't have a token and thereby fail.
Attempt 2:
I'm using an if controller to check whether the token has been set or not. I haven't got this working at all. I added a beanshell preprocessor to the controller where I attempt to retrieve the token. If it's null or empty I set the token variable to "". In the if controller I check for this value. But yeah. No luck yet.
Attempt 3
In Beanshell check if the token is created already. If not, call the test fragment that retrieves it. Unfortunately this seems not possible.
It might be worth noting that I store my tokens in a property, so that all threads can access it.
Please let me know if you need more information.
I figured out a solution. In essence what I tried to do is to store a token for each row in the data file.
I did this by creating a setUp Thread Group which is executed before other thread groups. In this I loop through the data and store a token for each. Now all other thread groups may access these as they run.
Related
I created PowerBI report which which is connecting to data source via API service. Returning json contains thousands of entities. API service is called via Web.Content function. API service returns always total record count and so we are able to calculate nr. of pages which has to be called to obtain whole dataset. This report is displaying data from our servicedesk app, which is deployed on many servers and for many customers and use Query parameters to connect to any of these servers.
Detail of Power query is below.
Why am I writing here. This report was working without any issue more than 1,5 year but on August 17th one of servers start causing erros in step Pages where are some random lines (pages) with errors - see attached picture labeled "Errors in step Pages". and this is reason that next step Entities (List.Union) in query is stopping refresh and generate errors with message:
Expression.Error: We cannot apply field access to the type List. Details: Value=[List] Key=requests
What is notable
API service si returning records in the same order but faulty lists are random when calling with same parameters
some times is refresh without any error
The same power query called on another server is working correctly , problem is only with one specific server.
This problem started without notice on the most important server after 1,5 year without any problem.
Here is full text power of query for this main source, which is used later in other queries to extract all necessary data. Json is really complicated and I extract from it list of requests, list of solvers, list of solver groups,.... and this base query and its output is input for many referenced queries.
Errors in step Pages
let
BaseAPIUrl = apiurl&"apiservice?", /*apiurl is parameter - name of server e.g. https://xxxx.xxxxxx.sk/ */
EntitiesPerPage = RecordsPerPage, /*RecordsPerPage is parameter and defines nr. of record per page - we used as optimum 200-400 record per pages, but is working also with 4000 record per page*/
ApiToken = FnApiToken(), /*this function is returning apitoken value which is returning value of another api service apiurl&"api/auth/login", which use username and password in body of call to get apitoken */
GetJson = (QParm) => /*definiton general function to get data from data source*/
let
Options =
[ Query= QParm,
Headers=
[
Accept="application/json",
ApiKeyName="apitoken",
Authorization=ApiToken
]
],
RawData = Web.Contents(BaseAPIUrl, Options),
Json = Json.Document(RawData)
in Json,
GetEntityCount = () => /*one times called function to get nr of records using GetJson, which is returned as a part of each call*/
let
QParm = [pp="1", pg="1" ],
Json = GetJson(QParm),
Count = Json[totalRecord]
in
Count,
GetPage = (Index) => /*repeatadly called function to get each page of json using GetJson*/
let
PageNr = Text.From(Index+1),
PerPage = Text.From(EntitiesPerPage),
QParm = [pg = PageNr, pp=PerPage],
Json = GetJson(QParm),
Value = Json[data][requests]
in Value,
EntityCount = List.Max({ EntitiesPerPage, GetEntityCount() }), /*setup of nr. of records to variable*/
PageCount = Number.RoundUp(EntityCount / EntitiesPerPage), /*setup of nr. of pages */
PageIndices = { 0 .. PageCount - 1 },
Pages = List.Transform(PageIndices, each GetPage(_) /*Function.InvokeAfter(()=>GetPage(_),#duration(0,0,0,1))*/), /*here we call for each page GetJson function to get whole dataset - there is in comment test with delay between getpages but was not neccessary*/
Entities = List.Union(Pages),
Table = Table.FromList(Entities, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
I also tried another way of appending pages to list using List.Generate. This is also bringing random errors in list but
it is bringing possibility to transform to table in contrast with original way with using List.Transform, but other referenced queries are failing and contains on the last row errors
When I am exploring content of faulty page/list extracting it via Add as New Query there are always all record without any fail.....
Source = List.Generate( /*another way to generate list of all pages*/
() => [Page = 0, ReqPageData = GetPage(0) ],
each [Page] < PageCount,
each [ReqPageData = GetPage( [Page] ),
Page = [Page] + 1 ],
each [ReqPageData]
),
#"Converted to Table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error), /*here i am able to generate table from list in contrast when is used List.Generate*/
#"Expanded Column1" = Table.ExpandListColumn(#"Converted to Table", "Column1"), /*here aj can expand list to column*/
#"Removed Errors" = Table.RemoveRowsWithErrors(#"Expanded Column1", {"Column1"}) /*here i try to exclude errors, but i dont know what happend and which records (if any) are excluded*/
Extracting errored page
and finnaly I am tottaly clueless not able to find the cause of this behavior on this specific server. I tested to call pages which are errored via POSTMAN, I discused this issue with author of API service and He also tried to call this API service with all parameters but server is returning every page OK, only Power query is not able to List.Transform ...
I will be grateful and appreciate any tips or advice or if somebody solved the same issue in the past ....
Kuby
No, each error line of list in step List.Transform coud by extracted as new query and there are all records from one page OK. hmmmm
Finnaly, problem described in this issue was caused by "corrupted" content of returning json. The provider of core system informed me that they found bug and after fixing on the side of servisdesk is everything OK again. I tried to find problem in Power query and problem was in servisdesk. :(
Is there a way to add each session Id that is retrieved from a Loop Controller to a list and assign it to a property for use in the following thread group? Below I used a couple of Dummy Sampler to explain my requirement.
I had 3 users stored in a list to retrieve 3 session ids in the setUp Thread Group.
JSR223 PreProcessor
List usernames = Arrays.asList('Peter', 'Alex', 'Mary');
props.put('accounts', usernames);
I was able to read a username from this property to get a session id in the response accordingly per iteration in the Loop Controller.
"sessionId": "this_is_my_session_id-${__groovy(props.get('accounts').get(${__jm__LoopController__idx} % 3),)}-${__jm__LoopController__idx} "
I parsed the 3 session ids out by a JSR223 PostProcessor
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
def jsonSlurper = new JsonSlurper();
def response = jsonSlurper.parseText(prev.getResponseDataAsString());
def json = JsonOutput.toJson(response.sessionId)
def sessionId = new JsonSlurper().parseText(json)
log.info('The session id is:' + sessionId)
ArrayList<String> sessionIds = new ArrayList<String>();
props.put("sessionIds", sessionIds.add(sessionId))
I needed to add these 3 session ids to a list and assign it to a property so that I can use one session id inside the property per VU/thread in the following Thread Group. But it didn't work as expected. It threw error saying No such property: sessionIds
${__groovy(props.get(sessionIds).get(${__jm__UseSession__idx} % 3),)}
We don't know what do you "expect"
Most probably the problem is here:
props.put("sessionIds", sessionIds.add(sessionId))
Collection.add() function returns a boolean value so it puts true to the sessionIds property instead of the real value of the ArrayList.
So I believe you need to change it to something like:
sessionIds.add(sessionId)
props.put("sessionIds", sessionIds)
if you're going to run the JSR223 Test Element in the loop you can also reconsider the way you're initializing the sessionIds and implement the following logic:
If sessionIds property exists - read its value
If it doesn't exist - create a new ArrayList
Something like:
ArrayList<String> sessionIds = props.get("sessionIds") ?: new ArrayList<String>()
More information on Groovy scripting in JMeter: Apache Groovy: What Is Groovy Used For?
We are trying to receive information about customers, all we get is 20 results, would like to search next page as well. Should loop through all pages until null, but can't create a loop when using the web explorer, or?
What value should be paste to receive all results? (Leaving pageToken field empty only gives us 20 results.
https://developers.google.com/admin-sdk/reseller/v1/reference/subscriptions/list#try-it
Your problem is that you are specifying fields=, but haven't included nextPageToken as one of the fields. That is why there is no nextPageToken present in the response. By adding nextPageToken (or omitting fields completely(v2) or setting fields=* (v3)), your first page of results will include a nextPageToken, which you will provide as the value of pageToken for your next call. Rinse, repeat until you get a response with no nextPageToken. For your first call, of course pageToken is blank.
In order to get more than 20 results, you have have to provide some value in "maxResults" parameter.
maxResults(unsigned integer): When retrieving a large list, the maxResults is the maximum number of results per page. The nextPageToken value takes you to the next page. The default is 20. Acceptable values are 1 to 100, inclusive.
Please check this page for reference: https://developers.google.com/admin-sdk/reseller/v1/reference/subscriptions/list
Hope that helps!
From one John to another...
I didn't call the REST HTTP endpoint directly, but when using the G Suite Directory API Client Library I needed to loop thru multiple pages to receive all results.
This is the pattern I used. It would be very similar for the G Suite Reseller API.
/// <summary>
/// List all Members in a Domain Group.
/// <param name="service">DirectoryService object (Google Directory API)</param>
/// <returns>Collection of Member emails</returns>
/// </summary>
public IEnumerable<string> ListGroupMembers(DirectoryService service)
{
// Set Group key (email address of the Group or id of the Group)
var groupKey = "email-for-google-group#domain-name.com";
// Define parameters of request (Group email)
MembersResource.ListRequest request = service.Members.List(groupKey);
// Sadly, this won't work
request.MaxResults = int.MaxValue;
// And the max page size of response is ONLY 200!
// So you have to check for the next page token
// and execute another request if there is one
do
{
// Get Members response for this Group
Members response = request.Execute();
// Return the emails in this response page
foreach (var member in response.MembersValue)
{
yield return member.Email;
}
// Get next page token
request.PageToken = response.NextPageToken;
// Continue loop if next page token is not null
} while (!string.IsNullOrEmpty(request.PageToken));
}
I'm trying to implement chained drop down boxes using the tutorial here. My classes are not as straight forward as the ones in the tutorial though.
I want to chain the drop down boxes for the create.gsp view in the Load class. Each load belongs to an account from the Account class, and each account belongs to a user from the User class, and each user has several cargo destinations from the Address class.
My goal is to have the cargo destination field up date based on which account is selected.
I am having trouble understanding the AJAX function in the tutorial (step 3), and how it relates to the Grails function (step 4).
Here is the AJAX code:
function respondToSelect(event)
{
new Ajax.Updater("memberSelect",
"/chainedSelect/family/updateSelect",
{method:'get', parameters: {selectedValue : $F("familySelect")} }
);
}
Here is the Grails method:
def updateSelect = {
def familySelected = Family.find("from Family as family where family.surname=:surname", [surname:params.selectedValue])
render (template:"selectMember", model : ['familySelected' : familySelected])
}
If someone could just explain what the third parameter of the AJAX function is doing I think I can figure the Grails part out.
{method:'get', parameters: {selectedValue : $F("account")}}
If someone could just explain what the third parameter of the AJAX
function is doing
The third argument is an object of parameters that get passed to the Updater that tell it how to make the HTTP request to the server.
Make the request an HTTP GET request:
method:'get'
Pass the following named query parameters:
{selectedValue: $F("account")}
$F is a prototype shortcut to retrieve the value of an element. In this case, it's getting the selected value of the DOM element with id account.
This ultimately results in something like the following request:
GET /chainedSelect/family/updateSelect?selectedValue=someValue
Where "someValue" is the currently-selected item in the "account" select list.
Now I know i can only dowload a string asynchronously in Windows Phone Seven, but in my app i want to know which request has completed.
Here is the scenario:
I make a certain download request using WebClient()
i use the following code for download completed
WebClient stringGrab = new WebClient();
stringGrab.DownloadStringCompleted += ClientDownloadStringCompleted;
stringGrab.DownloadStringAsync(new Uri(<some http string>, UriKind.Absolute));
i give the user the option of giving another download request if this request takes long for the user's liking.
my problem is when/if the two requests return, i have no method/way of knowing which is which i.e. which was the former request and which was second!
is there a method of knowing/sychronizing the requests?
I can't change the requests to return to different DownloadStringCompleted methods!
Thanks in Advance!
Why not do something like this:
void DownloadAsync(string url, int sequence)
{
var stringGrab = new WebClient();
stringGrab.DownloadStringCompleted += (s, e) => HandleDownloadCompleted(e, sequence);
stringGrab.DownloadStringAsync(new Uri(url, UriKind.Absolute));
}
void HandleDownloadCompleted(DownloadStringCompletedEventArgs e, int sequence)
{
// The sequence param tells you which request was completed
}
It is an interesting question because by default WebClient doesn't carry any unique identifiers. However, you are able to get the hash code, that will be unique for each given instance.
So, for example:
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri("http://www.microsoft.com", UriKind.Absolute));
WebClient client2 = new WebClient();
client2.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client2.DownloadStringAsync(new Uri("http://www.microsoft.com", UriKind.Absolute));
Each instance will have its own hash code - you can store it before actually invoking the DownloadStringAsync method. Then you will add this:
int FirstHash = client.GetHashCode();
int SecondHash = client2.GetHashCode();
Inside the completion event handler you can have this:
if (sender.GetHashCode() = FirstHash)
{
// First completed
}
else
{
// Second completed
}
REMEMBER: A new hash code is given for every re-instantiation.
If the requests are essentially the same, rather than keep track of which request is being returned. Why not just keep track of if one has previously been returned? Or, how long since the last one returned.
If you're only interested in getting this data once, but are trying to allow the user to reissue the request if it takes a long time, you can just ignore all but the first successfully returned result. This way it doesn't matter how many times the user makes additional requests and you don't need to track anything unique to each request.
Similarly, if the user can request/update data from the remote service at any point, you could keep track of how long since you last got successfull data back and not bother updating the model/UI if you get another resoponse shortly after that. It'd be preferable to not make requests in this scenario but if you've got to deal with long delays and race conditions in responses you could use this technique and still keep the UI/data up to date within a threshold of a few minutes (or however long you specify).