Nemesis
Nemesis

Reputation: 123

Load testing an API which uses oauth token for authorization using loadimpact k6

I'm trying to load test an API (GET method) using loadimpact k6 which requires oauth token for authorization to get the successful response. I already have a postman collection file which does this by running pre-request script. The pre-request script will request token from the authorization server and then populates the token in the environment variable. I used the "Postman to LoadImpact converter" to generate the k6 script but it isn't doing any help. The script fails to get the access token.

The generated script from the converter is given below:

// Auto-generated by the Load Impact converter

import "./libs/shim/core.js";

export let options = { maxRedirects: 4 };

const Request = Symbol.for("request");
postman[Symbol.for("initial")]({
  options,
  collection: {
    currentAccessToken: "",
    "Client-Id": "",
    "Client-Secret": "",
    "Token-Scope": "",
    "Grant-Type": "client_credentials",
    "Access-Token-URL": "",
    accessTokenExpiry: ""
  }
});

export default function() {
  postman[Request]({
    name: "Collection Mock",
    id: "",
    method: "GET",
    address:
      "",
    headers: {
      Authorization: "Bearer {{currentAccessToken}}"
    },
    pre() {
      const echoPostRequest = {
        url: pm.environment.get("Access-Token-URL"),
        method: "POST",
        header: "Content-Type:x-www-form-urlencoded",
        body: {
          mode: "urlencoded",
          urlencoded: [
            { key: "client_id", value: pm.environment.get("Client-Id") },
            {
              key: "client_secret",
              value: pm.environment.get("Client-Secret")
            },
            { key: "grant_type", value: pm.environment.get("Grant-Type") },
            { key: "scope", value: pm.environment.get("Token-Scope") }
          ]
        }
      };

      var getToken = true;

      if (
        !pm.environment.get("accessTokenExpiry") ||
        !pm.environment.get("currentAccessToken")
      ) {
        console.log("Token or expiry date are missing");
      } else if (
        pm.environment.get("accessTokenExpiry") <= new Date().getTime()
      ) {
        console.log("Token is expired");
      } else {
        getToken = false;
        console.log("Token and expiry date are all good");
      }

      if (getToken === true) {
        pm.sendRequest(echoPostRequest, function(err, res) {
          console.log(err ? err : res.json());
          if (err === null) {
            console.log("Saving the token and expiry date");
            var responseJson = res.json();
            pm.environment.set("currentAccessToken", responseJson.access_token);

            var expiryDate = new Date();
            expiryDate.setSeconds(
              expiryDate.getSeconds() + responseJson.expires_in
            );
            pm.environment.set("accessTokenExpiry", expiryDate.getTime());
          }
        });
      }
    }
  });
}

The issue is with pm.sendRequest which is not supported by the converter and I'm not sure what the alternative is. So, I'm looking for ways to dynamically request access token from the authorization server and use that token to make a request to the API for load testing in k6 script.

Upvotes: 3

Views: 5667

Answers (2)

Nemesis
Nemesis

Reputation: 123

I ended up using below code snippet to make a successful call for my purpose:

// Auto-generated by the Load Impact converter

import "./libs/shim/core.js";
import http from "k6/http";
import { check, sleep } from "k6";

export let options = {
  max_vus: 10,
  vus: 10,
  stages: [
    { duration: "1m", target: 10 }
  ]
}

const Request = Symbol.for("request");

pm.environment.set("currentAccessToken", "");
pm.environment.set("accessTokenExpiry", "");
pm.environment.set("clientId", "");
pm.environment.set("clientSecret", "");
pm.environment.set("tokenScope", "");
pm.environment.set("grantType", "");
pm.environment.set("accesstokenUrl", "");

pm.environment.set("apiUrl", "");

pm.environment.set("subscriptionKeys", "");

export default function() {     
    var getToken = true;

    if (!pm.environment.get("accessTokenExpiry") || !pm.environment.get("currentAccessToken")) {
        //console.log("Token or expiry date are missing");
    } else if (pm.environment.get("accessTokenExpiry") <= new Date().getTime()) {
        //console.log("Token is expired");
    } else {
        getToken = false;
        //console.log("Token and expiry date are all good");
    }

    if (getToken === true) {
        //get the access token first
        let res = http.post(pm.environment.get("accesstokenUrl"), {
        "client_id": pm.environment.get("clientId"),
        "client_secret": pm.environment.get("clientSecret"),
        "grant_type": pm.environment.get("grantType"),
        "scope": pm.environment.get("tokenScope")
        });

        var checkRes = check(res, {
            "Token Request status is 200": (r) => r.status === 200,
        });

        if (checkRes) {
            var responseJson = res.json();
            pm.environment.set("currentAccessToken", responseJson.access_token);
            var expiryDate = new Date();
            expiryDate.setSeconds(
              expiryDate.getSeconds() + responseJson.expires_in
            );
            pm.environment.set("accessTokenExpiry", expiryDate.getTime());
        }

        sleep(1);

        //make the api request using the access token and subscription keys (if required)
        let apiRes = http.get(pm.environment.get("apiUrl"),
        { 
            headers: { "Authorization": "Bearer " + pm.environment.get("currentAccessToken"), 
                       "Subscription-Key" : pm.environment.get("subscriptionKeys") 
            } 
        });
        check(apiRes, {
            "API Request status is 200": (res) => res.status === 200
        });

        sleep(3);
    }
}

Upvotes: 3

As you have seen sendRequest is not supported ...

This is primarily because of the fact pm.sendRequest is asynchronous but k6 at this point doesn't have a event loop so ... no asynchronous http calls :( (except with http.batch but ... not I find it unlikely that you want this to be asynchronous or ... well you can't do it with k6 at this point either way ... you can just rewrite it to use k6's http.post

As far as I can see this should look like

pre() {

      var getToken = true;

      if (
        !pm.environment.get("accessTokenExpiry") ||
        !pm.environment.get("currentAccessToken")
      ) {
        console.log("Token or expiry date are missing");
      } else if (
        pm.environment.get("accessTokenExpiry") <= new Date().getTime()
      ) {
        console.log("Token is expired");
      } else {
        getToken = false;
        console.log("Token and expiry date are all good");
      }

      if (getToken === true) {
          let res = http.post(pm.environment.get("Access-Token-URL"), {
           "client_id": pm.environment.get("Client-Id") ,
           "client_secret": pm.environment.get("Client-Secret"),
           "grant_type": pm.environment.get("Grant-Type"),
           "scope": pm.environment.get("Token-Scope")
          });
          console.log(err ? err : res.json());
          if (err === null) {
            console.log("Saving the token and expiry date");
            var responseJson = res.json();
            pm.environment.set("currentAccessToken", responseJson.access_token);

            var expiryDate = new Date();
            expiryDate.setSeconds(
              expiryDate.getSeconds() + responseJson.expires_in
            );
            pm.environment.set("accessTokenExpiry", expiryDate.getTime());
          }
    }

Disclaimer: I have never used postman and the code above was written/copy-pasted by hand and not tested :)

Upvotes: 1

Related Questions