使用 loadimpact k6 对使用 oauth 令牌进行授权的 API 进行负载测试

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

我正在尝试使用 loadimpact k6 对 API(GET 方法)进行负载测试,这需要 oauth 令牌进行授权才能获得成功的响应。我已经有一个邮递员收集文件,它通过 运行 预请求脚本执行此操作。预请求脚本将从授权服务器请求令牌,然后将令牌填充到环境变量中。我使用 "Postman to LoadImpact converter" 生成了 k6 脚本,但它没有任何帮助。脚本无法获取访问令牌。

转换器生成的脚本如下:

// 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());
          }
        });
      }
    }
  });
}

问题出在转换器不支持的 pm.sendRequest 上,我不确定替代方案是什么。因此,我正在寻找从授权服务器动态请求访问令牌并使用该令牌向 API 发出请求以在 k6 脚本中进行负载测试的方法。

如您所见,不支持 sendRequest ...

这主要是因为 pm.sendRequest is asynchronous but k6 at this point doesn't have a event loop so ... no asynchronous http calls :( (except with http.batch 但...不是 我发现你不太可能希望它是异步的,或者......好吧你现在不能用 k6 来做......你可以重写它以使用 k6's http.post

据我所知,这应该是

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());
          }
    }

免责声明:我从未使用过邮递员,上面的代码是 written/copy-pasted 手工编写的,未经测试:)

我最终使用以下代码片段成功调用了我的目的:

// 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);
    }
}