【问题标题】:How to use a single OAuth2.0 token for Multiple Virtual Users in a Gatling load test如何在 Gatling 负载测试中为多个虚拟用户使用单个 OAuth2.0 令牌
【发布时间】:2019-04-04 17:24:25
【问题描述】:

我需要通过 Gatling(我完全是新手!)对需要 OAuth2.0 令牌的 API 进行负载测试,但希望每个虚拟用户都使用相同的令牌。我正在检索令牌(我认为)并将其放入一个名为“访问”的变量中,但是当测试本身开始时,我不断收到“没有定义名为“访问”的属性”。

我的令牌检索如下所示(连同 httpConf,在下面使用):

 class MySimulation extends Simulation {

 val httpConf = http        
    .baseUrl("https://MyBaseUrl.Com/")
    .acceptHeader("application/json") 
    .doNotTrackHeader("1")
    .acceptLanguageHeader("en-UK,en;q=0.5")
    .acceptEncodingHeader("gzip, deflate")
    .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")
    .shareConnections

 val header = Map("Content-Type" -> """application/x-www-form-urlencoded""")

 al auth = scenario("Retrieve Token")
 .exec(http("POST OAuth Req")
 .post("https://SomeTokenUrl")
 .formParam("resource", "someresource")
 .formParam("grant_type", "somegranttype")
 .formParam("client_secret", "someclientsecret")
 .formParam("client_id", "someclientid")
 .headers(header).check(status.is(200)).check(jsonPath("$.access_token").find.saveAs("access"))) 

然后我尝试将负载测试设置为(注意:我最初确实放置了“映射”,而不是可变变体,但在某处读取默认值是不可变的,并想知道这是否是标头无法更新的原因) :

 val headers_10 = scala.collection.mutable.Map("Content-Type" -> "application/json; charset=ISO-8859-1", "Authorization" -> "Bearer ${access}")

 val scn = scenario("MyService Gatling test run")       
           .exec(http("")               
           .post("Myservice/api")
           .headers(headers_10.toMap)                
           .body(StringBody("""{"SomeProperty": "Some Value"}"""))
           .asJson
           .check(status.is(200)))

 setUp(
    auth.inject(constantUsersPerSec(1) during (2 seconds)),
    scn.inject(nothingFor(2 seconds),
    constantUsersPerSec(10) during (10 seconds) 
    ).protocols(httpConf))
    .assertions(global.responseTime.max.lt(500)) 
    .assertions(forAll.failedRequests.percent.lte(1)) 
    .assertions(global.responseTime.mean.lte(100)) 

这个想法是令牌检索将在负载测试开始之前完成,然后负载测试场景将使用“访问”变量,但它给出了以下结果:

  ERROR : Failed to build request: No attribute named 'access' is defined 

我已经走到了尽头。我猜这可能与范围有关,也许变量不会延续到负载测试场景,但我在其他地方看到过似乎完全推荐该设置的示例,所以我不知道这些其他示例是部分完整的还是什么。

【问题讨论】:

    标签: scala load-testing gatling jsonpath


    【解决方案1】:

    今天我为我的项目实现了这个场景。请查看下面的代码,它也适用于您。

    注意:我已使用 API 所需的参数编写了此代码。您可以根据需要修改此代码。目前,此代码编写在一个类中。我已经以适当的格式实现了这段代码,并使用了不同的类和属性文件。我也会发布那个。

    对于单个类,代码如下:

    `
    
    package aapi
    
        import io.gatling.core.Predef._
        import io.gatling.http.Predef._
    
         class manifestSimulation extends Simulation {    
    
        private var token = ""
    
        object authAvi{
         // This is the request(API) which we are going to use for generating the auth token 1 time and then will feed this token into subsequent request.
         var postBody = "{\"username\":\"devusername\”,\”password\”:\”devpassword”}”
    
        val auth = scenario("Retrieve our auth Token which will be used in the subsequent request“)
            .exec(
                http("POST OAuth Req")
                .post(“User Post URL“)
                .body(StringBody(postBody))
                .header("ClientId", “test”)
        .header("DSN", “devDB”)
        .header("accept", "application/json")
        .header("Accept-Language", "en-us")
        .header("Content-Type", "application/json")
                .check(status.is(200))
        .check(jsonPath("$.token")
        .saveAs("token")))
                .exitHereIfFailed
                .exec{session => { token = session("token").as[String]
                                 session}}       
        }
        object manifest {
         // This is the request(API) which we are going to hit multiple times using the token which we generated from the previous auth API
    
            var manifestHeaders = Map("ClientId" -> “test”, "DSN" -> "devDB", "Token" -> "${token}")
    
            val manifestMethod = exec(session => session.set("token", token))            
                .exec(http("Manifest Details")              
                    .get(“Your get URL“)
                    .headers(manifestHeaders)
                    .check(status.is(200))
                     )
        }   
    
        val scn = scenario(“**********This is your actual load test*******************”)
            .exec(manifest.manifestMethod)
           setUp(
          authAvi.auth.inject(constantUsersPerSec(1) during (1 seconds)), // fire 1 requests per second for 1 second to retrieve token
        scn.inject(nothingFor(4 seconds), // waits 4 seconds as a margin to process token and this time varies for every user
            constantUsersPerSec(5) during (5 seconds))) // fire 5 requests per second for 5 seconds which will result in 25 (5*5) requests and overall 26 requests when the report gets generated (because we have 1 request for auth token and 25 requests of our intended API (25+1 = 26)
    
         }`
    

    【讨论】:

      【解决方案2】:

      会话是每个用户的,用户之间不会共享会话数据。因此,虽然您有 1 个用户运行“auth”场景并保存令牌,但运行“scn”的是两个不同的用户,他们无权访问 auth 用户的会话值。

      不建议这样做,但您可以通过将 auth 令牌推送到常规 scala var 并在 auth 场景中设置它并在主要场景中读取它来解决这个问题 - 您只需要确保 auth 始终在之前完成你注入任何其他用户。

      var token: String = ""
      

      那么在auth场景中,最后有一个步骤比如

      .exec(session => {
          token = session("access").as[String]
          session
      })
      

      然后在 scn 场景开始时有一个步骤来设置会话变量

      .exec(session.set("access", token))
      

      我过去曾使用过这种模式并且它有效,但我确信有更好的方法来做到这一点

      【讨论】:

      • 感谢您的回答 James,但我仍然无法让它正常工作。我的问题是 'token = session.get("access").as[String]' 位。它不识别'get'。然后我尝试了'.exec(session => { token = session("access")})',这可以理解地给出了类型不匹配,因为令牌是字符串,并且它返回了一个 SessionAttribute 类型。然后我再次将 '.as[String]' 读到末尾,现在我得到了不同的类型不匹配 - 现在说它找到了 'unit' 类型,但期待 'Validation' ?!?!
      • 嗨詹姆斯 - 发现了问题。在 Gatling 的更高版本中,“get”不再是会话 API 的一部分,这是我正在使用的版本(另外,我认为它需要是大括号,而不是会话周围的普通括号!)。如果您想编辑您的答案以提及您可能需要.exec{session => { token = session("access").as[String] session}} 如果您使用的是 Gatling 的更高版本,我会将其标记为正确答案。感谢您的帮助!
      【解决方案3】:

      我也在尝试这个场景,但是当我尝试在 scn 场景开始时添加 .exec{session => { token = session("access").as[String] session}} 时出现错误.

             val scn = scenario("MyService Gatling test run")
             .exec{session => { token = session("access").as[String] session}}  
            .exec(http("")               
             .post("Myservice/api")
             .headers(headers_10.toMap)                
             .body(StringBody("""{"SomeProperty": "Some Value"}"""))
             .asJson
             .check(status.is(200)))
      

      我还分别尝试了以下步骤的更改:-

        setUp(auth.inject(constantUsersPerSec(1) during (2 seconds)),
        .exec{session => { token = session("access").as[String] session}}
        scn.inject(nothingFor(2 seconds),
        constantUsersPerSec(10) during (10 seconds) 
        ).protocols(httpConf))
        .assertions(global.responseTime.max.lt(500)) 
        .assertions(forAll.failedRequests.percent.lte(1)) 
        .assertions(global.responseTime.mean.lte(100)) 
      

      但他们都没有工作。你能建议我把这段代码放在哪里吗?

      【讨论】:

      • 嗨 Tarun - 请参阅下面的答案。希望这将涵盖您正在寻找的内容。
      【解决方案4】:

      @塔伦,

      当我这样做时,我在我的场景中使用了“exec”,而不是设置,并使用了以下语法:

       val dataToUse = feed(testData)
       .exec(session => session.set("access", token))            
       .exec(http("")             
       .post("*the_URL_to_send_to)*")
       .headers(headers_10.toMap)
       .body(RawFileBody("${filePath}")).asJson
       .check(status.is(200))
                   )
      

      正如前面讨论的 cmets 中提到的,这是因为我使用的是更高版本的 gatling 并且“get”方法不再是会话 api 的一部分。

      我的完整解决方案如下 - 请注意,需要注意的一些事项可能不适用于您的解决方案。我使用了一个对象,因为它只是让我对我想做的事情更清楚!此外,一些导入可能是多余的,因为我将它们作为 scattergun 方法的一部分包含在内,以找到有用的东西!

      最后,我基本上列出了一个目录的内容,并循环浏览其中列出的文件,将每个文件用作馈送器。你看起来好像在使用 json 的文字模板,所以可能不需要它,但我想我会为了完整性而包含它,因为它非常方便 - 如果你更改 json 的格式,你不需要需要在模拟中更改模板,您只需清除目录并将新格式的示例放在那里就可以了! :

       package myTest
      
       import io.gatling.core.Predef._
       import io.gatling.http.Predef._
       import scala.concurrent.duration._
       import scala.collection.JavaConversions._
       import java.io.File
       import java.io.FileNotFoundException
      
      
       class myTestSimulation extends Simulation {    
      
      
       val httpConf = http
          .baseUrl("*your_base_URL*")
      .acceptHeader("application/json") // Here are the common headers
      .doNotTrackHeader("1")
      .acceptLanguageHeader("en-UK,en;q=0.5")
      .acceptEncodingHeader("gzip, deflate")
      .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")
      .shareConnections
      
      val header = Map("Content-Type" -> """application/x-www-form-urlencoded""");
      private var token = ""
      
      val auth = scenario("Retrieve Token")
          .exec(
              http("POST OAuth Req")
              .post("*URL_for_Token*")
              .formParam("resource", "*your_resource_value*")
              .formParam("grant_type", "*your_grant_type*")
              .formParam("client_secret", "*your_client_secret_value*")
              .formParam("client_id", "*your_client_id_value*")
              .headers(header)
              .check(status.is(200)).check(jsonPath("$.access_token").find.saveAs("access")))  
              .exec{session => { token = session("access").as[String]
                               session}}       
      
      object myTestObject {
      
          var headers_10 = scala.collection.mutable.Map("Content-Type" -> "application/json; charset=ISO-8859-1", "Authorization" -> "Bearer ${access}")     
          val testData = Iterator.continually(
          new File("*pathway_to_file*") match {
            case d if d.isDirectory => d.listFiles.map(f => Map("filePath" -> f.getPath))
            case _ => throw new FileNotFoundException("Samples path must point to directory")
          }).flatten
      
          val myTestObjectMethod = feed(testData)
              .exec(session => session.set("access", token))            
              .exec(http("")              
                  .post("*the_URL_to_send_to(don't_forget_that_base_URL_above_is_automatically_stuck_to_the_front_of_this!)*")
                  .headers(headers_10.toMap)
                  .body(RawFileBody("${filePath}")).asJson
                  .check(status.is(200))
                   )
      }   
      
      val scn = scenario("my_actual_load_test")
          .exec(myTestSimulation.myTestObject)
         setUp(
        auth.inject(constantUsersPerSec(1) during (1 seconds)), // fire 1 requests per second for 1 second to retrieve token
      scn.inject(nothingFor(2 seconds), // waits 2 seconds as a margin to process token
          constantUsersPerSec(50) during (300 seconds) // fire 50 requests per second for 300 seconds
         ).protocols(httpConf))                             
         .assertions(global.responseTime.max.lt(500)) // set max acceptable response time
         .assertions(forAll.failedRequests.percent.lte(1)) // less than 1% of tests should fail
         .assertions(global.responseTime.mean.lte(100)) // set average response time
       }
      

      我的意思是,当我从中删除敏感内容时,我可能在某处打错了字,但希望这能满足您的需要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-16
        相关资源
        最近更新 更多