Performance testing is a non-functional software testing technique that determines how the stability, speed, scalability, and responsiveness of an application holds up under a given workload.
Steps
Figure out crucial user journey
Estimate load (typical hour, peak hour, off-hour)
Simulate user journey
Analysis performance report (max, min, mean, p95, 99p response time, fail rate, fail reason)
Come up with actions, improvements
How to write performance test using Gatling
Scenario
To represent users’ behaviors
val onBoarding = scenario("On boarding") .feed(onBoardingCustomerFeeder) .exec { session -> session.set("timestamp", "${MILLISECONDS.toMicros(System.currentTimeMillis())}") } .exec(CustomerManager.getConsents) .exec(CustomerManager.customerProspect) .exec(IamManager.createCredentialId) .exec(Vida.fetchChallenge) .exec(Vida.registerPublicKey) .exec(CustomerManager.requestAaiSdkLicense) .exec(CustomerManager.getBreadcrumbs) .exec(CustomerManager.saveCustomerPreferences) .exec(CustomerManager.fetchCountries) .exec(CustomerManager.signUp) .exec { session -> session.set("otp", retrieveOtp(session.getString("countryCode"), session.getString("phone"))) } .exec(CustomerManager.verifyOtp)
Simulation
A simulation is a description of the load test. It describes how, possibly several, user populations will run: which scenario they will execute and how new virtual users will be injected.
class OnBoardingPerfTest : Simulation() { init { setUp(onBoarding.injectOpen(rampUsers(10).during(5))) .protocols(httpProtocol) } }
Session
Each virtual user is backed by a Session. Those Sessions are the actual messages that go down the scenario workflow. A Session is basically a state placeholder, where testers can inject or capture and store data.
val onBoarding = scenario("On boarding") .feed(onBoardingCustomerFeeder) ...... .exec(CustomerManager.signUp) // retrieve otp and store it in session .exec { session -> session.set("otp", retrieveOtp(session.getString("countryCode"), session.getString("phone"))) } .exec(CustomerManager.verifyOtp)
object IamManager { val createCredentialId = http("Create credential id") .post("iam-manager/credential") .body(StringBody("""{"customerId": "#{customerId}"}""")) .check( status().shouldBe(201), jsonPath("$.state").shouldBe("IN_PROGRESS"), //Store credential id in session which will be used later jsonPath("$.credentialId").exists().saveAs("credentialId"), ) }
Feeder
Feeders are a convenient API for testers to inject data from an external source into the virtual users’ sessions.
object Feeders { private const val phoneChars = "0123456789" private val toneOfVoice = listOf("FORMAL", "FRIENDLY") private val languages = listOf("TAGLISH", "ENGLISH") val onBoardingCustomerFeeder = generateSequence { val (silentPrivateKey, silentPublicKey) = CryptoUtil.generateKeyPairBase64Encoded() val (presencePrivateKey, presencePublicKey) = CryptoUtil.generateKeyPairBase64Encoded() mapOf( "email" to "perf-test-${UUID.randomUUID()}@advancegroup.com", "phone" to (1..10).map { phoneChars.random() }.joinToString(""), "silentPrivateKey" to silentPrivateKey, "silentPublicKey" to silentPublicKey, "presencePrivateKey" to presencePrivateKey, "presencePublicKey" to presencePublicKey, "preferenceToneOfVoice" to toneOfVoice.random(), "preferenceLanguage" to languages.random() ) }.iterator() }
Check
A check is a response processor that captures some part of it and verifies that it meets some given condition(s).
val customerProspect = http("Customer prospect") .post("customer-manager/v1/customers/prospect") .body(StringBody("""{"consentIds":#{consentIds}}""")) .header("X-Idempotency-Key") { UUID.randomUUID().toString() } .check( // Check http status code status().shouldBe(200), // Check response body jsonPath("$.id").exists().saveAs("customerId"), jsonPath("$.status").shouldBe("PROSPECT") )
Assertion
Assertions are used to define acceptance criteria on Gatling statistics (e.g. 99th percentile response time) that would make Gatling fail and return an error status code for the test as a whole.
class OnBoardingPerfTest : Simulation() { init { setUp(onBoarding.injectOpen(rampUsers(5).during(5))) .protocols(httpProtocol) .assertions( global().responseTime().percentile(99.0).lte(2000), global().responseTime().max().lt(5000), global().failedRequests().count().shouldBe(0) ) } }
Report
By default, reports are automatically generated at the end of a simulation. They consist of HTML files.
Demo
https://github.com/SafiBank/PerfTest
References
https://gatling.io/docs/gatling/reference/current/core/concepts/
Attachments:
Screen Shot 2022-12-05 at 10.52.40.png (image/png)