海北藏族自治州网站建设_网站建设公司_Figma_seo优化
2026/1/19 17:39:54 网站建设 项目流程

一、Scenario基础概念

Scenario定义

Scenario是Gatling中的概念,用来模拟真实用户在系统中的完整操作流程。每个Scenario代表一类用户的行为模式。

import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ class RealUserSimulation extends Simulation { // HTTP配置 val httpProtocol = http .baseUrl("https://api.zmtests.com") .acceptHeader("application/json") .userAgentHeader("Gatling/3.9") .disableCaching // 定义用户操作流程 val userJourney = scenario("真实用户操作流") .exec(http("首页访问") .get("/home") .check(status.is(200)) .check(jsonPath("$.userId").saveAs("userId"))) .pause(2, 5) // 随机思考时间2-5秒 .exec(http("登录操作") .post("/login") .formParam("username", "test_user") .formParam("password", "password123") .check(status.is(200)) .check(jsonPath("$.authToken").saveAs("authToken"))) .pause(1, 3) }

二、复杂用户行为建模

多步骤业务流程

val ecommerceUser = scenario("电商购物用户") .exec( // 第一阶段:浏览阶段 group("浏览阶段") { exec(http("访问首页") .get("/") .check(status.is(200))) .pause(3, 7) .exec(http("浏览商品列表") .get("/products?category=electronics") .check(status.is(200)) .check(css(".product-item", "data-id").findAll.saveAs("productIds"))) .pause(2, 4) .exec(http("查看商品详情") .get("/products/${productIds.random()}") .check(status.is(200))) .pause(5, 10) // 仔细阅读商品详情 } ) .exec( // 第二阶段:购买阶段 group("购买阶段") { exec(http("添加购物车") .post("/cart/add") .header("Authorization", "Bearer ${authToken}") .body(StringBody("""{"productId": "${productId}", "quantity": 1}""")) .asJson .check(status.is(201))) .pause(1, 2) .exec(http("查看购物车") .get("/cart") .header("Authorization", "Bearer ${authToken}") .check(status.is(200)) .check(jsonPath("$.totalPrice").saveAs("cartTotal"))) .pause(2, 3) .exec(http("结算") .post("/checkout") .header("Authorization", "Bearer ${authToken}") .body(StringBody("""{"paymentMethod": "credit_card"}""")) .asJson .check(status.is(200)) .check(jsonPath("$.orderId").saveAs("orderId"))) } )

文章来源:卓码软件测评

精彩推荐:点击蓝字即可
▲软件负载测试
▲API自动化测试▲软件测试▲第三方软件测试▲软件性能测试▲软件测试机构

条件分支逻辑

val conditionalUser = scenario("条件流程用户") .exec(http("访问网站") .get("/") .check(status.is(200))) .pause(2) // 条件分支:检查用户是否登录 .doIf(session => !session("userId").asOption[String].isDefined) { exec(http("新用户注册") .post("/register") .formParam("email", "user_${userId}@test.com") .formParam("password", "Test123!") .check(status.is(201)) .check(jsonPath("$.userId").saveAs("userId"))) .pause(1) } // 根据用户类型执行不同操作 .doIfEquals("${userType}", "premium") { exec(http("访问VIP专区") .get("/vip/content") .check(status.is(200))) .pause(5, 10) } .doIfEquals("${userType}", "regular") { exec(http("浏览普通内容") .get("/regular/content") .check(status.is(200))) .pause(2, 5) } // 循环浏览多个页面 .repeat(5, "pageNum") { exec(http("浏览第${pageNum}页") .get("/content?page=${pageNum}") .check(status.is(200))) .pause(1, 2) }

三、高级用户行为模式

数据驱动测试

// 使用Feeder提供测试数据 val userFeeder = csv("data/users.csv").circular val productFeeder = jsonFile("data/products.json").random val dataDrivenUser = scenario("数据驱动用户") .feed(userFeeder) .feed(productFeeder) .exec(http("使用动态数据登录") .post("/login") .formParam("username", "${username}") .formParam("password", "${password}") .check(status.is(200)) .check(jsonPath("$.sessionId").saveAs("sessionId"))) .pause(2) .exec(http("查看个性化推荐") .get("/recommendations?userId=${userId}") .header("Session-Id", "${sessionId}") .check(status.is(200)) .check(jsonPath("$.recommendations[*].productId").findAll.saveAs("recommendedProducts"))) .pause(3) .foreach("${recommendedProducts}", "product") { exec(http("查看推荐产品${product}") .get("/products/${product}") .check(status.is(200))) .pause(1) }

复杂业务流程

val businessWorkflowUser = scenario("复杂业务流程用户") .tryMax(3) { // 重试机制 exec( group("文档处理流程") { exec(http("上传文档") .post("/documents/upload") .header("Authorization", "Bearer ${token}") .bodyPart(RawFileBodyPart("file", "test.pdf")) .check(status.is(202)) .check(jsonPath("$.documentId").saveAs("docId"))) .pause(1) .asLongAs(session => session("processingStatus").asOption[String].exists(_ != "COMPLETED"), "processingStep" ) { exec(http("检查处理状态") .get("/documents/${docId}/status") .check(status.is(200)) .check(jsonPath("$.status").saveAs("processingStatus"))) .pause(2) } .exec(http("下载处理结果") .get("/documents/${docId}/download") .check(status.is(200))) .pause(3) } ) }.exitHereIfFailed // 如果失败则退出场景

四、真实用户模拟配置

多用户类型混合

// 定义不同用户类型的场景 val casualUser = scenario("轻度用户") .exec(commonActions.randomPageVisits(1, 3)) .pause(30, 60) val powerUser = scenario("重度用户") .exec(commonActions.deepEngagementFlow) .pause(10, 20) .exec(commonActions.complexOperations) .pause(5, 15) val adminUser = scenario("管理员用户") .exec(adminActions.dashboardAccess) .pause(2, 5) .exec(adminActions.userManagement) .pause(1, 3) .exec(adminActions.systemMonitoring) .pause(5, 10) // 混合用户注入策略 setUp( casualUser.inject( rampUsers(100).during(2.minutes), // 前2分钟逐步增加100个轻度用户 constantUsersPerSec(2).during(5.minutes) // 然后保持每秒2个用户 ), powerUser.inject( rampUsers(20).during(1.minute), // 逐步增加20个重度用户 constantUsersPerSec(0.5).during(10.minutes) ), adminUser.inject( constantUsersPerSec(0.1).during(15.minutes) // 持续有管理员操作 ) ).protocols(httpProtocol) .maxDuration(15.minutes) .assertions( global.responseTime.max.lt(2000), global.successfulRequests.percent.gt(95) )

用户思考时间和行为随机化

val realisticUser = scenario("带思考时间的真实用户") .exec(api.login) // 正态分布思考时间 .pause(normalPauses(5.seconds, 2.seconds)) .exec(api.browseProducts) // 均匀分布思考时间 .pause(uniformPauses(3.seconds, 7.seconds)) .randomSwitch( 70.0 -> exec(api.addToCart), // 70%概率添加购物车 20.0 -> exec(api.saveForLater), // 20%概率收藏 10.0 -> exec(api.continueBrowsing) // 10%概率继续浏览 ) .pause(2, 5) .randomSwitch( 60.0 -> exec(api.checkout), // 60%概率结账 40.0 -> exec(api.abandonCart) // 40%概率放弃购物车 ) // 使用会话变量控制流程 .doIf(session => session("cartValue").as[Double] > 100) { exec(api.applyCoupon) // 购物车价值大于100时使用优惠券 .pause(1) } .uniformRandomSwitch( exec(api.standardShipping), // 随机选择配送方式 exec(api.expressShipping), exec(api.pickupInStore) )

五、性能测试

模块化设计

object UserActions { val browseProducts = exec( group("产品浏览") { exec(http("获取产品列表") .get("/api/products") .queryParam("page", "${currentPage}") .check(status.is(200)) .check(jsonPath("$[*].id").findAll.saveAs("productIds"))) .pause(1) .foreach("${productIds}", "productId") { exec(http("查看产品详情") .get("/api/products/${productId}") .check(status.is(200))) .pause(0.5, 1.5) } } ) val searchProducts = exec( group("产品搜索") { feed(searchKeywordsFeeder) .exec(http("搜索产品") .get("/api/search") .queryParam("q", "${keyword}") .queryParam("sort", "${sortBy}") .check(status.is(200))) .pause(2, 4) } ) val purchaseFlow = exec( group("购买流程") { exec(http("创建订单") .post("/api/orders") .body(StringBody( """{ | "items": ${cartItems}, | "shippingAddress": "${address}", | "paymentMethod": "${paymentMethod}" |}""".stripMargin)) .asJson .check(status.is(201)) .check(jsonPath("$.orderId").saveAs("orderId"))) .pause(1) .exec(http("确认订单") .post("/api/orders/${orderId}/confirm") .check(status.is(200))) } ) } // 组合使用模块 val completeUserJourney = scenario("完整用户操作") .exec(UserActions.browseProducts) .pause(3) .exec(UserActions.searchProducts) .pause(2) .exec(UserActions.purchaseFlow)

错误处理和恢复

val resilientUser = scenario("弹性用户") .exec(http("访问首页") .get("/") .check(status.is(200)) .check(header("X-Request-ID").saveAs("requestId"))) .pause(2) // 处理可能的错误 .tryMax(2, "登录重试") { exec(http("用户登录") .post("/login") .body(StringBody("""{"username":"${user}", "password":"${pass}"}""")) .asJson .check(status.in(200, 201)) .check(jsonPath("$.token").saveAs("authToken"))) }.exitHereIfFailed .doIf(session => session("authToken").asOption[String].isDefined) { exec(http("获取用户资料") .get("/profile") .header("Authorization", "Bearer ${authToken}") .check(status.is(200)) // 如果失败,回退到基础数据 .checkIf(status.is(200)) { jsonPath("$.premiumFeatures").saveAs("features") }.checkIf(status.is(404)) { jsonPath("$.basicFeatures").saveAs("features") }) } // 记录错误但不中断测试 .exec(session => { if (session.isFailed) { println(s"请求失败但继续执行: ${session("requestId").asOption[String]}") } session })

六、监控验证

验证和断言

val validatedScenario = scenario("带验证的用户流程") .exec( http("创建资源") .post("/api/resources") .body(StringBody("""{"name": "test-resource", "type": "document"}""")) .asJson .check(status.is(201)) .check(jsonPath("$.id").saveAs("resourceId")) .check(jsonPath("$.createdAt").transform(dateStr => java.time.Instant.parse(dateStr).toEpochMilli ).saveAs("creationTime")) .check(header("Location").saveAs("resourceLocation")) ) .pause(1) .exec( http("验证资源创建") .get("${resourceLocation}") .check(status.is(200)) .check(jsonPath("$.id").is("${resourceId}")) .check(jsonPath("$.status").is("active")) .check(jsonPath("$.name").is("test-resource")) // 验证时间戳在合理范围内 .check(jsonPath("$.createdAt").transform(dateStr => val created = java.time.Instant.parse(dateStr).toEpochMilli val now = System.currentTimeMillis() (now - created) < 60000 // 创建时间应在1分钟内 ).is(true)) ) .exec( http("执行复杂操作") .put("/api/resources/${resourceId}/process") .check(status.is(202)) .check(header("X-Operation-Id").notNull) .check(bodyString.saveAs("responseBody")) // 自定义验证 .check(bodyString.transform(body => { val json = io.circe.parser.parse(body).getOrElse(io.circe.Json.Null) val status = json.hcursor.downField("status").as[String].getOrElse("") status == "processing" || status == "queued" }).is(true)) )

性能监控点

val monitoredScenario = scenario("带监控的用户流程") .exec( group("关键业务事务") { exec( http("事务开始") .get("/api/transaction/start") .check(status.is(200)) ).pause(1) .exec( http("处理步骤1") .post("/api/transaction/step1") .check(status.is(200)) .check(responseTimeInMillis.lt(1000)) // 响应时间断言 ) .exec( http("处理步骤2") .post("/api/transaction/step2") .check(status.is(200)) .check(responseTimeInMillis.lt(2000)) ) .exec( http("事务提交") .post("/api/transaction/commit") .check(status.is(200)) .check(jsonPath("$.transactionId").saveAs("txId")) ) }.resources( // 监控相关资源加载 http("加载CSS") .get("/static/css/app.css"), http("加载JS") .get("/static/js/app.js") ) )

七、电商平台用户模拟案例

class ECommerceSimulation extends Simulation { val httpProtocol = http .baseUrl("https://www.zmtests.com") .acceptHeader("application/json") .acceptEncodingHeader("gzip, deflate") .userAgentHeader("Mozilla/5.0") .disableCaching // 数据供给 val userAccounts = csv("data/users.csv").circular val products = jsonFile("data/products.json").random val searchTerms = csv("data/search_terms.csv").random // 用户行为对象 object Browse { val homepage = exec(http("访问首页") .get("/") .check(status.is(200)) .check(css("meta[name='csrf-token']", "content").saveAs("csrfToken"))) .pause(2, 5) val category = exec( feed(products) .exec(http("浏览分类") .get("/category/${categoryId}") .check(status.is(200)) .check(css(".product-card", "data-id").findRandom.saveAs("viewedProduct"))) .pause(3, 8) ) val productDetail = exec( exec(http("查看产品详情") .get("/product/${viewedProduct}") .check(status.is(200)) .check(css("#product-price").saveAs("productPrice"))) .pause(5, 15) // 仔细查看产品 ) } object Search { val performSearch = exec( feed(searchTerms) .exec(http("搜索产品") .get("/search") .queryParam("q", "${searchTerm}") .check(status.is(200)) .check(css(".search-result-item").count.saveAs("resultCount"))) .pause(1, 3) ) val filterResults = exec( exec(http("筛选结果") .get("/search/filter") .queryParam("q", "${searchTerm}") .queryParam("priceMin", "0") .queryParam("priceMax", "100") .check(status.is(200))) .pause(2, 4) ) } object Cart { val addToCart = exec( exec(http("添加到购物车") .post("/cart/add") .header("X-CSRF-Token", "${csrfToken}") .formParam("productId", "${viewedProduct}") .formParam("quantity", "1") .check(status.is(200)) .check(jsonPath("$.cartTotal").saveAs("cartTotal"))) .pause(1, 2) ) val viewCart = exec( exec(http("查看购物车") .get("/cart") .check(status.is(200)) .check(css(".cart-item").count.gt(0))) .pause(2, 3) ) } object Checkout { val startCheckout = exec( exec(http("开始结账") .post("/checkout/start") .header("X-CSRF-Token", "${csrfToken}") .check(status.is(200)) .check(jsonPath("$.checkoutId").saveAs("checkoutId"))) .pause(1, 2) ) val submitOrder = exec( exec(http("提交订单") .post("/checkout/${checkoutId}/complete") .header("X-CSRF-Token", "${csrfToken}") .body(StringBody("""{ "shippingMethod": "standard", "paymentMethod": "credit_card", "billingAddress": ${addressJson} }""")).asJson .check(status.is(200)) .check(jsonPath("$.orderNumber").saveAs("orderNumber"))) .pause(2, 5) ) } // 定义完整用户流程 val standardUser = scenario("标准用户") .feed(userAccounts) .exec(Browse.homepage) .randomSwitch( 60.0 -> exec(Browse.category), 40.0 -> exec(Search.performSearch) ) .pause(2, 5) .exec(Browse.productDetail) .randomSwitch( 30.0 -> exec(Cart.addToCart), // 30%的用户添加购物车 70.0 -> exec(Search.performSearch) // 70%继续浏览 ) .doIf(session => session("cartTotal").asOption[String].isDefined) { exec(Cart.viewCart) .randomSwitch( 50.0 -> exec(Checkout.startCheckout), // 50%结账 50.0 -> exec(session => { println(s"用户放弃购物车: ${session("userId").as[String]}") session }) ) } // 设置负载模式 setUp( standardUser.inject( nothingFor(5.seconds), // 热身期 rampUsers(50).during(30.seconds), // 逐步增加 constantUsersPerSec(2).during(5.minutes), // 稳定负载 rampUsersPerSec(2).to(10).during(2.minutes), // 压力测试 constantUsersPerSec(10).during(3.minutes), // 峰值负载 rampUsersPerSec(10).to(2).during(1.minute) // 恢复期 ) ).protocols(httpProtocol) .maxDuration(15.minutes) .assertions( global.responseTime.percentile3.lt(800), // 99%请求<800ms global.responseTime.max.lt(3000), global.failedRequests.percent.lt(1.0), forAll.responseTime.percentile4.lt(500) // 所有请求的95%<500ms ) }

八、总结

设计原则

真实:模拟真实用户行为,包括思考时间、错误处理、随机选择

模块化:将常用操作封装为可重用组件

数据驱动:使用外部数据源模拟多样化用户

渐进加载:从低负载开始,逐步增加压力

监控验证:添加断言和监控点

调试技巧

// 添加调试信息 val debugScenario = scenario("调试场景") .exec(session => { println(s"当前用户: ${session("userId").asOption[String]}") println(s"会话属性: ${session.attributes}") session }) .exec(http("测试请求") .get("/debug") .check(bodyString.saveAs("response")) .check(status.is(200))) .exec(session => { println(s"响应内容: ${session("response").as[String]}") session }) // 使用Gatling Recorder记录真实用户操作 // gatling-recorder --help

性能优化建议

连接池配置:优化HTTP连接重用

资源清理:及时关闭不用的连接

缓存策略:合理使用缓存减少重复请求

断言优化:避免过于复杂的断言影响性能

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询