Мне нужно загрузить и протестировать API, для которого требуется токен OAuth2.0 через Gatling (в этом я совсем новичок!), но хотел бы, чтобы каждый виртуальный пользователь использовал один и тот же токен. Я получаю токен в порядке (я думаю) и помещаю его в переменную с именем «доступ», но я продолжаю получать «атрибут с именем «доступ» не определен», когда начинается сам тест.
Мой поиск токена выглядит следующим образом (вместе с 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))
Идея заключалась в том, что извлечение токена завершится до запуска нагрузочного теста, а переменная «access» затем будет использоваться сценарием нагрузочного теста, но это дает следующий результат:
ERROR : Failed to build request: No attribute named 'access' is defined
Я достиг конца своей привязи с этим. Я предполагаю, что это может быть как-то связано с областями, и, возможно, переменная не переносится в сценарий нагрузочного теста, но я видел примеры в другом месте, которые, похоже, рекомендуют именно эту настройку, поэтому я не знаю являются ли эти другие примеры частично полными или что.





Сеансы для каждого пользователя, и никакие данные сеанса не передаются пользователям. Таким образом, хотя у вас есть 1 пользователь, запускающий ваш сценарий «auth» и сохраняющий токен, это два разных пользователя, которые запускают «scn», и у них нет доступа к значениям сеанса пользователя auth.
Это не рекомендуется, но вы можете решить эту проблему, вставив токен аутентификации в обычную переменную scala, установив this в сценарии аутентификации и прочитав его в основном сценарии - вам просто нужно убедиться, что аутентификация всегда завершается, прежде чем вводить какие-либо другие пользователи.
var token: String = ""
затем в сценарии авторизации сделайте шаг в конце, например
.exec(session => {
token = session("access").as[String]
session
})
затем в начале сценария scn есть шаг для установки переменной сеанса
.exec(session.set("access", token))
Я использовал этот шаблон в прошлом, и он работает, но я уверен, что есть более приятные способы сделать это.
Привет Джеймс - нашел проблему. 'get' больше не является частью сеансового API в более поздних версиях Gatling, которую я использую (плюс, я думаю, что это должны быть фигурные скобки, а не обычные скобки вокруг сеанса!). Если вы хотите отредактировать свой ответ, упомянув, что вам может понадобиться .exec{session => { token = session("access").as[String] session}} , если вы используете более позднюю версию Gatling, я отмечу это как правильный ответ. Спасибо за вашу помощь!
Я также пробую этот сценарий, но получаю сообщение об ошибке при попытке добавить .exec{session => {token = session("access").as[String] session}} в начале сценария scn.
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))
но ни один из них не работал. Не могли бы вы предложить мне, где я должен на самом деле поместить этот код.
Привет Тарун - см. ответ ниже. Надеюсь, это покроет то, что вы искали.
@Тарун,
Когда я это сделал, у меня в сценарии был '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))
)
Как упоминалось в комментариях в предыдущем обсуждении, это произошло потому, что я использовал более позднюю версию гатлинга, а метод «получить» больше не был частью API сеанса.
Мое полное решение было следующим: обратите внимание, что есть ряд вещей, на которые следует обратить внимание, которые могут не относиться к вашему решению. Я использовал объект, так как он просто прояснил для меня то, что я пытался сделать! Кроме того, некоторые импорты, вероятно, избыточны, так как я включил их как часть бессистемного подхода к поиску того, что сработало!
Наконец, я в основном перечисляю содержимое каталога и циклически просматриваю перечисленные в нем файлы, используя каждый из них в качестве источника. Вы выглядите так, как будто используете буквальный шаблон 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
}
Я имею в виду, что я, вероятно, сделал опечатку где-то в строке, когда удалял из нее конфиденциальные вещи, но, надеюсь, это сделает то, что вам нужно.
Сегодня я реализовал этот сценарий для своего проекта. Пожалуйста, посмотрите код ниже, и он будет работать и для вас.
Примечание. Я написал этот код с необходимыми параметрами моего 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)
}`
Спасибо за ваш ответ, Джеймс, но я все еще не могу заставить его работать. Моя проблема заключается в бите 'token = session.get("access").as[String]. Он не распознает «получить». Затем я попробовал '.exec(session => { token = session("access")})', что по понятным причинам дало несоответствие типа, поскольку токен был строкой, и он возвращал тип SessionAttribute. Затем я снова прочитал «.as[String]» до конца, и теперь я получаю другое несоответствие типов — теперь он говорит, что нашел тип «единица», но ожидал «проверки»?!?!