Selenium IDE进阶玩法:用命令行运行器搞定多浏览器并行测试与结果分析(含避坑指南)
Selenium IDE进阶玩法用命令行运行器搞定多浏览器并行测试与结果分析含避坑指南当你的测试套件从几十个案例扩展到数百个时单纯依靠Selenium IDE的图形界面回放已经无法满足效率需求。这时命令行运行器selenium-side-runner就像给你的测试装备了涡轮增压——通过并行执行、精准过滤和智能报告三大核心能力将测试效率提升300%不再是幻想。本文将带你解锁那些官方文档没细说的实战技巧比如如何避免多进程资源竞争导致的幽灵失败以及怎样从JSON报告中挖出真正的性能瓶颈。1. 环境配置的隐藏陷阱与高效搭建方案很多团队在初次部署命令行运行器时会卡在环境配置这个看似简单的环节。你以为npm install之后就能畅行无阻下面这些实战经验可能让你少走半天弯路。1.1 浏览器驱动的版本玄学不同浏览器驱动版本间的兼容性差异堪称Selenium测试中的暗礁区。特别是当你的团队混合使用Mac和Windows机器时这个问题会更加明显# 推荐的安全安装方式指定稳定版本 npm install -g chromedriver89.0.0 npm install -g geckodriver0.29.0版本匹配黄金法则ChromeDriver大版本必须与Chrome浏览器主版本一致GeckoDriver每季度更新一次即可除非用到新特性在CI环境中固定版本号避免自动升级带来意外踩坑记录某次Chrome自动更新后测试突然大面积失败最终发现是ChromeDriver版本滞后导致。现在我们的CI流程中会强制校验版本匹配。1.2 网络环境下的代理突围在企业内网环境中npm安装经常因为代理设置败下阵来。下面这个组合拳能解决90%的网络问题# 设置npm代理根据实际网络调整 npm config set proxy http://corp-proxy:8080 npm config set https-proxy http://corp-proxy:8080 npm config set strict-ssl false # 验证驱动下载地址 chromedriver --version如果依然失败可以手动下载驱动二进制文件然后添加到系统PATH# Mac/Linux示例 export PATH$PATH:/path/to/drivers # Windows需要永久修改环境变量2. 并行测试的性能压榨艺术默认的并行执行策略可能让你的CPU飙到100%但测试效率却提升有限。通过精细化控制进程和资源分配才能真正发挥多核优势。2.1 进程数设置的动态平衡-w参数不是越大越好这个公式可以帮助确定最优值推荐进程数 CPU核心数 × (1 平均测试I/O等待时间/CPU计算时间)实际执行时可以这样动态调整# 根据测试类型动态设置进程数 if [[ $TEST_TYPE UI ]]; then WORKERS4 else WORKERS$(( $(nproc) * 2 )) fi selenium-side-runner -w $WORKERS tests/*.side不同场景下的进程配置建议测试类型CPU密集型网络I/O密集型混合型4核机器4868核机器81612带Selenium GridGrid节点数×1.5Grid节点数×2Grid节点数×1.82.2 资源竞争的破解之道并行测试中最头疼的就是共享资源冲突比如同一个测试用户被多个进程同时使用临时文件路径冲突数据库记录重复插入我们的解决方案是引入动态隔离// 在测试套件前置脚本中 const workerId process.env.TEST_WORKER_INDEX || 0; exports.testUser autotest_${workerId}company.com; exports.tempDir ./temp/${workerId};配合运行时参数注入selenium-side-runner --params workerId${TEST_WORKER_INDEX}3. 精准测试筛选与智能报告解析当你有500测试用例时如何快速执行关键路径测试又怎样从海量报告中定位真正的问题这些技巧让测试报告会说话。3.1 正则过滤的进阶用法--filter参数支持正则表达式但有些高级用法文档没提# 执行所有带smoke标签的测试 selenium-side-runner --filter smoke # 执行优先级为P0或P1的测试 selenium-side-runner --filter (P0|P1) # 排除性能测试 selenium-side-runner --filter ^(?!.*perf).*$更智能的做法是将过滤规则保存在配置文件中# .side-filter.yml default: smoke or P0 ci: not local然后通过脚本动态加载FILTER$(grep $ENV .side-filter.yml | cut -d -f2-) selenium-side-runner --filter $FILTER3.2 报告数据的二次加工JUnit格式的报告可以直接被Jenkins解析但JSON报告才是数据分析的宝库。这个Python脚本可以提取关键指标import json with open(results/project.json) as f: data json.load(f) metrics { total: len(data[testResults]), passed: sum(1 for r in data[testResults] if not r[status]), duration: sum(r[duration] for r in data[testResults]), slow_tests: sorted( [(r[name], r[duration]) for r in data[testResults]], keylambda x: x[1], reverseTrue)[:5] } print(f通过率: {metrics[passed]/metrics[total]:.1%}) print(f最慢测试TOP5: {metrics[slow_tests]})将上述脚本集成到CI流程中可以自动生成这样的质量门禁报告[质量报告] 本次构建 #123 ✅ 通过率: 98.6% (287/291) 性能瓶颈: 1. checkout流程 (12.4s) 2. 支付验证 (8.7s) 3. 搜索过滤 (6.2s)4. Grid集群的实战调优指南当单机无法满足测试需求时Selenium Grid是必然选择。但官方的配置建议往往过于保守无法发挥硬件最大潜力。4.1 节点配置的黄金比例经过上百次实验我们总结出这些配置经验Docker容器资源配置节点类型CPU内存最大会话数浏览器实例Chrome1核1GB53Firefox1核1.5GB32Edge2核2GB42对应的docker-compose配置示例services: chrome-node: image: selenium/node-chrome:4.1.0 environment: - SE_NODE_MAX_SESSIONS5 - SE_NODE_OVERRIDE_MAX_SESSIONStrue resources: limits: cpus: 1 memory: 1G4.2 会话复用的性能魔术默认情况下每个测试都会新建浏览器实例其实可以通过会话复用提升3倍速度selenium-side-runner \ --params reuseSessiontrue \ --server http://grid-hub:4444需要配套的测试清理策略// 在测试套件的teardown中 if (reuseSession) { await driver.manage().deleteAllCookies(); await driver.executeScript(sessionStorage.clear();); } else { await driver.quit(); }这种模式下需要注意测试之间必须有完善的清理机制不适合涉及浏览器全局配置的测试需要监控内存泄漏5. 异常处理与稳定性提升那些随机出现的幽灵失败最让人头疼。通过下面这些策略我们把测试稳定性从85%提升到了99%。5.1 元素定位的弹性策略在动态页面中硬编码的等待时间往往是失败的根源。改用这种智能等待方案// 自定义waitForElement函数 async function waitForElement(selector, timeout30) { const endTime Date.now() timeout * 1000; while (Date.now() endTime) { const el await driver.findElement(selector).catch(() null); if (el await el.isDisplayed()) return el; await driver.sleep(500); } throw new Error(元素 ${selector} 超时未找到); }在Selenium IDE中可以通过执行脚本的方式调用Command: execute script Target: return waitForElement(idlogin-btn) Value: loginButton5.2 失败自动重试机制在CI管道中加入这样的重试逻辑MAX_RETRY3 RETRY_DELAY60 for ((i1; i$MAX_RETRY; i)); do selenium-side-runner --output-formatjunit results.xml if grep -q failures\0\ results.xml; then break fi echo 第 $i 次重试中... sleep $RETRY_DELAY done配合测试用例的幂等设计使用UUID作为测试数据每个测试独立清理环境避免依赖执行顺序6. 持续集成中的最佳实践将Selenium IDE测试无缝接入CI/CD流水线需要解决这些特有的挑战。6.1 高效的依赖管理使用Docker镜像预装所有依赖FROM node:14 RUN npm install -g selenium-side-runner chromedriver COPY drivers /usr/local/bin ENV PATH/usr/local/bin:${PATH}Jenfile中的典型用法pipeline { agent { docker { image your-registry/selenium-runner:latest args -v /dev/shm:/dev/shm } } stages { stage(Test) { steps { sh selenium-side-runner -w 8 --output-formatjunit tests/*.side junit results/*.xml } } } }6.2 智能化的截图策略在失败时自动截屏并附加到测试报告中// 在全局afterEach中 if (testFailed) { const screenshot await driver.takeScreenshot(); const path screenshots/${testName}.png; fs.writeFileSync(path, screenshot, base64); attachScreenshotToReport(path); }配合Allure报告可以生成这样的可视化结果7. 性能监控与优化闭环测试不仅要验证功能更应该成为性能优化的指南针。7.1 关键指标采集通过Selenium的performance日志获取真实用户指标await driver.log(performance).then((entries) { const metrics entries.map((e) JSON.parse(e.message)); const paintMetrics metrics.filter((m) m.message.method Page.frameScheduledNavigation); });典型的前端性能指标指标名称健康阈值采集方式First Paint1sPerformance APIDOM Content Loaded2sNavigation TimingJS Heap Used50MBMemory API7.2 自动化性能基准在CI中建立性能基准门禁# 运行性能测试套件 selenium-side-runner --filter perf --output-formatjson perf.json # 校验关键指标 python check_perf.py --baseline baseline.json --current perf.json当检测到性能回退时自动通知团队[性能告警] checkout流程超时 当前值: 2.4s (基准: 1.8s) 可能原因: - 新增了第三方跟踪脚本 - 购物车API响应变慢 建议行动: 1. 检查New Relic的APM数据 2. 回滚最近前端部署8. 移动端测试的特殊考量虽然主要是为Web设计但通过一些技巧也能应对移动端测试需求。8.1 模拟移动设备通过Chrome的移动仿真模式selenium-side-runner -c goog:chromeOptions.mobileEmulation{ deviceName: iPhone X }关键仿真参数对照表参数桌面测试移动测试viewportSize1920x1080375x812userAgent桌面UAiOS/Android UAtouchEventsfalsetruedevicePixelRatio138.2 真机测试方案通过Appium连接到本地设备selenium-side-runner \ --server http://localhost:4723/wd/hub \ -c browserNamesafari platformNameiOS platformVersion15.4需要额外配置iOS需要安装WebDriverAgentAndroid需要启用USB调试推荐使用物理设备而非模拟器9. 安全测试的集成技巧将安全扫描融入常规UI测试提前发现漏洞。9.1 自动化XSS检测在表单提交后插入检查脚本async function checkXSS() { const alerts await driver.executeScript( return window.alerts || []; ); if (alerts.length 0) { throw new Error(发现XSS漏洞: ${alerts.join(, )}); } }9.2 敏感数据监控在测试中自动检查const pageSource await driver.getPageSource(); const sensitivePatterns [ /[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}/, // 信用卡 /[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Z|a-z]{2,}/ // 邮箱 ]; sensitivePatterns.forEach((pattern) { if (pattern.test(pageSource)) { console.warn(发现敏感数据暴露风险); } });10. 测试数据管理的艺术好的测试数据策略能让维护成本降低70%。10.1 动态数据生成使用Faker.js创建测试数据// 在测试前置脚本中 const faker require(faker); exports.testUser { name: faker.name.findName(), email: faker.internet.email(), address: faker.address.streetAddress() };10.2 数据快照技术对关键流程保存数据快照# 保存测试数据 selenium-side-runner --params snapshottrue # 回放时使用快照 selenium-side-runner --params useSnapshotsnapshot_20230301.json快照文件示例{ login: { validUser: { username: test_user, password: Passw0rd! } }, products: { featured: [1001, 1002] } }11. 可视化测试的进阶方案超越传统的DOM检查实现真正的像素级验证。11.1 智能截图对比使用Resemble.js进行视觉回归测试const compare require(resemblejs).compare; const screenshot await driver.takeScreenshot(); compare(screenshot, baseline, (diff) { if (diff.misMatchPercentage 0.1) { fs.writeFileSync(diff.png, diff.getBuffer()); } });11.2 布局稳定性检测通过JavaScript检测CLS布局偏移const cls await driver.executeScript( return window.__CLS_LOG__ || []; ); assert(cls.every(entry entry.value 0.1), 布局偏移超标);12. 测试报告的增强现实让静态报告变成行动指南。12.1 交互式报告看板使用ElasticSearchKibana构建// 报告数据映射 { mappings: { properties: { testName: { type: keyword }, duration: { type: float }, status: { type: keyword }, failureReason: { type: text }, screenshot: { type: keyword } } } }12.2 智能失败分析通过机器学习分类失败模式from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans # 加载历史失败信息 failures load_failure_messages() # 特征提取 vectorizer TfidfVectorizer() X vectorizer.fit_transform(failures) # 自动分类 kmeans KMeans(n_clusters5) clusters kmeans.fit_predict(X)输出分类结果示例失败模式分类报告: 1. 元素未找到 (38%) 2. 超时错误 (25%) 3. 验证失败 (20%) 4. 网络问题 (12%) 5. 其他 (5%)13. 测试代码的模块化设计即使不用编程也能实现高度复用的测试结构。13.1 共享步骤封装在.side文件中定义可复用的命令序列{ id: login, commands: [ { command: type, target: idusername, value: ${username} }, { command: type, target: idpassword, value: ${password} } ] }13.2 数据驱动测试通过JSON文件驱动参数化测试// tests/login_cases.json [ { username: standard_user, password: secret_sauce, expected: /inventory.html }, { username: locked_user, password: secret_sauce, expected: errorlocked } ]在测试中通过execute script读取const testData JSON.parse(fs.readFileSync(login_cases.json)); return testData;14. 跨平台测试的统一方案一套测试兼容Windows、Mac和Linux。14.1 路径处理的兼容技巧使用path模块处理文件路径// 在execute script中 const path require(path); const downloadDir path.join(__dirname, downloads);14.2 系统特性的条件执行通过能力检测决定执行路径const isMac await driver.executeScript( return navigator.platform.includes(Mac) ); if (isMac) { // Mac特有操作 } else { // Windows/Linux操作 }15. 无障碍测试的集成让自动化测试覆盖WCAG标准。15.1 自动化无障碍扫描使用axe-core注入检测await driver.executeScript( const axe require(axe-core); axe.run().then(results { window.__a11yResults__ results; }); ); const violations await driver.executeScript( return window.__a11yResults__.violations || []; );15.2 键盘导航测试模拟残障用户操作流await driver.actions() .sendKeys(Key.TAB) .sendKeys(Key.TAB) .sendKeys(Key.ENTER) .perform();16. 测试环境的动态编排根据测试需求智能准备环境。16.1 按需数据库准备在测试前执行数据准备// 前置脚本 const { Pool } require(pg); const pool new Pool({/* 配置 */}); await pool.query( INSERT INTO users VALUES (test_user, active) );16.2 微服务模拟使用Mock Service Worker拦截APIawait driver.executeScript( const { setupWorker } require(msw); const worker setupWorker( rest.get(/api/user, (req, res, ctx) { return res(ctx.json({ name: Mock User })) }) ); worker.start(); );17. 测试用例的智能生成让AI辅助创建更全面的测试。17.1 基于用户行为的测试生成分析生产环境用户轨迹# 从点击流数据生成测试用例 def generate_test(clicks): test { commands: [] } for click in clicks: test[commands].append({ command: click, target: fcss[data-id{click[element]}] }) return test17.2 变异测试技术自动生成边界条件测试function generateEdgeCases(input) { return [ input 1, input - 1, input * 10, input / 10, ]; }18. 测试资产的管理策略建立企业级的测试资源管理体系。18.1 版本控制集成Git钩子自动校验测试用例#!/bin/sh # pre-commit hook selenium-side-runner --verify tests/*.side || exit 118.2 测试用例的元数据管理在.side文件中添加元信息{ meta: { author: QA Team, priority: P0, lastUpdated: 2023-03-01 } }19. 测试即文档的文化建设让测试用例成为活的文档。19.1 自动化生成操作手册从.side文件生成Markdowndef generate_doc(side_file): with open(side_file) as f: data json.load(f) md f# {data[name]}\n\n for cmd in data[commands]: md f- {cmd[command]} {cmd[target]}\n return md19.2 测试与需求的追溯在测试用例中关联需求ID{ comment: 验证用户登录功能 (REQ-123) }20. 测试效能的全景监控建立测试健康度的量化体系。20.1 关键效能指标看板使用Grafana展示测试健康度 (通过率 × 0.4) (执行速度 × 0.3) (缺陷发现率 × 0.3)20.2 测试资产ROI分析计算投入产出比测试ROI (发现的缺陷数 × 平均修复成本) / (测试维护成本 执行成本)21. 测试团队的协作创新打破孤岛建立高效协作流程。21.1 测试用例的同行评审GitHub中的评审流程# .github/workflows/review.yml on: pull_request jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkoutv2 - run: selenium-side-runner --review ${PR_FILES}21.2 测试知识库建设使用Wiki维护常见问题## 元素定位失败 可能原因 1. 页面加载延迟 → 增加等待 2. 动态ID → 改用CSS选择器 3. iframe嵌套 → 先切换frame22. 面向未来的测试架构为即将到来的技术变革做好准备。22.1 WebComponents测试方案自定义元素的定位策略await driver.executeScript( class MyElement extends HTMLElement {} customElements.define(my-element, MyElement); ); const element await driver.findElement( cssmy-element::part(inner) );22.2 渐进式Web应用测试检测PWA特性const isPWA await driver.executeScript( return serviceWorker in navigator window.matchMedia((display-mode: standalone)).matches );23. 测试左移的实践路径让测试更早介入开发流程。23.1 API契约测试在UI测试前验证接口const response await fetch(/api/login, { method: POST, body: JSON.stringify({ user, pass }) }); assert(response.status 200);23.2 设计稿对比测试通过像素比对发现UI偏差from PIL import Image, ImageChops def compare_images(actual, expected): diff ImageChops.difference(actual, expected) return diff.getbbox() is None24. 测试右移的生产监控将测试能力延伸到生产环境。24.1 合成监控测试定期运行关键路径测试# 生产环境轻量级测试 selenium-side-runner --filter monitor --base-url $PROD_URL24.2 用户会话回放分析真实用户遇到的错误// 注入录制脚本 await driver.executeScript( window.__recordSession true; );25. 测试工程师的效能革命用这些工具提升个人生产力。25.1 IDE插件生态推荐必备插件Selenium IDE Enhancer增强元素定位Test Data Generator快速创建测试数据Smart Waits智能等待策略25.2 CLI生产力工具常用命令别名alias test-smokeselenium-side-runner --filter smoke alias test-perfselenium-side-runner -c goog:chromeOptions.args[headless] alias test-allselenium-side-runner -w \$(nproc) tests/*.side26. 测试报告的叙事技巧让枯燥的数据讲出动人故事。26.1 问题影响度评估定义失败严重等级def calculate_impact(failure): if checkout in failure.test_name: return CRITICAL elif login in failure.test_name: return HIGH else: return MEDIUM26.2 可视化时间线用Gantt图展示测试进度测试执行时间线: [ login.side ] 12s [ cart.side ] 5s [ checkout.side ] 18s27. 测试策略的动态调整根据质量趋势智能优化。27.1 风险基测试选择基于代码变更分析# 获取变更影响的模块 CHANGED_MODULES$(git diff --name-only | xargs -n1 dirname | sort -u) # 选择相关测试 selenium-side-runner --filter (${CHANGED_MODULES//\//|})27.2 测试时长的自适应控制根据CI时间预算调整import math def select_tests(available_time): tests load_all_tests() selected [] remaining available_time for test in sorted(tests, keylambda x: -x[priority]): if test[duration] remaining: selected.append(test) remaining - test[duration] return selected28. 测试环境的按需供给云原生时代的测试基础设施。28.1 Kubernetes测试集群Helm部署Selenium Grid# values.yaml nodes: chrome: replicas: 5 resources: limits: cpu: 1 memory: 1Gi28.2 函数式计算测试无服务器测试执行# 在AWS Lambda中运行 aws lambda invoke --function-name test-runner \ --payload {sideFile: tests/login.side} \ output.json29. 测试数据的安全合规满足GDPR等法规要求。29.1 数据脱敏技术在测试中自动脱敏const sensitiveFields [email, phone]; await driver.executeScript( document.querySelectorAll(input).forEach(input { if (${JSON.stringify(sensitiveFields)}.includes(input.name)) { input.value REDACTED; } }); );29.2 测试数据生命周期自动化清理流程pytest.fixture(scopefunction) def clean_test_data(): setup_test_data() yield cleanup_test_data() # 自动删除生成的数据30. 测试文化的建设路径从工具到思维的全面升级。30.1 质量大使计划每月评选标准发现关键缺陷数测试用例贡献度创新测试方案数30.2 测试黑客松24小时创新挑战最佳AI测试方案最有创意bug发现最高效测试工具31. 测试指标的反模式这些指标可能误导团队。31.1 测试用例数陷阱质量不等于数量# 不良指标 test_count len(glob.glob(tests/*.side)) # 更好指标 test_coverage calculate_business_flow_coverage()31.2 通过率的假象要分析失败模式def analyze_failures(failures): patterns { 元素未找到: 0, 验证失败: 0, 超时: 0 } for failure in failures: for pattern in patterns: if pattern in failure.message: patterns[pattern] 1 return patterns32. 测试自动化的投资回报构建业务案例的方法。32.1 成本节约计算自动化收益模型年度节约 (手工测试时间 - 维护时间) × 时薪 × 执行频率32.2 质量提升度量缺陷预防效果质量提升 生产缺陷下降率 × 平均修复成本33. 测试工具链的整合打造无缝衔接的工作流。33.1 IDE与缺陷跟踪集成自动提交缺陷// 测试失败时 if (testFailed) { await jira.createIssue({ project: WEB, summary: 测试失败: ${testName}, description: errorMessage, screenshot: await driver.takeScreenshot() }); }33.2 监控告警联动失败自动通知# CI失败时触发 if [[ $TEST_RESULT ! 0 ]]; then curl -X POST -H Content-Type: application/json \ -d {text:UI测试失败请检查构建 #$BUILD_NUMBER} \ $SLACK_WEBHOOK fi34. 测试资产的重构策略保持测试套件的健康度。34.1 重复代码检测查找相似测试用例def find_duplicate_tests(tests): hashes {} duplicates [] for test in tests: h hash(json.dumps(test[commands])) if h in hashes: duplicates.append((hashes[h], test[name])) else: hashes[h] test[name] return duplicates34.2 测试分层优化金字塔结构调整原结构 UI测试: 300 API测试: 50 单元测试: 200 目标结构 UI测试: 100 API测试: 200 单元测试: 50035. 测试团队的技术雷达持续评估新技术。35.1 技术评估框架四个维度评分| 技术 | 成熟度 | 适用性 | 学习曲线 | 整合难度 | |-------------|--------|--------|----------|----------| | Playwright | ★★★★☆ | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ | | Cypress | ★★★★★ | ★★★☆☆ | ★★☆☆☆ | ★★★☆☆ |35.2 渐进式采用策略分阶段引入小范围概念验证非关键业务试点核心业务推广全面采用36. 测试文档的智能检索快速找到所需信息。36.1 自然语言搜索使用NLP技术from sentence_transformers import SentenceTransformer model SentenceTransformer(all-MiniLM-L6-v2) query_embedding model.encode(如何测试支付流程) doc_embeddings model.encode(test_docs) similarities cosine_similarity(query_embedding, doc_embeddings)36.2 知识图谱构建测试概念关联元素定位 --uses-- CSS选择器 --uses-- XPath --related-- 等待策略37. 测试学习的个性化推荐基于行为的智能推荐。37.1 技能差距分析对比岗位要求def analyze_skills(engineer, requirements): gaps {} for skill, level in requirements.items(): if engineer.skills.get(skill, 0) level: gaps[skill] level - engineer.skills[skill] return gaps37.2 自适应学习路径推荐系统def recommend_courses(gaps): courses load_courses() return sorted( [c for c in courses if c.skill in gaps], keylambda x: -gaps[x.skill] )[:3]38. 测试创新的激励机制鼓励突破性思考。38.1 创新提案流程提交创意提案技术委员会评审分配资源实验成果推广38.2 质量创新奖项评选维度业务影响技术难度可推广性团队协作39. 测试转型的变革管理平滑过渡到新范式。39.1 能力提升路线图分阶段培训阶段1: Selenium IDE基础 (2周) 阶段2: 高级测试设计 (4周) 阶段3: 测试架构思维 (6周)39.2 变革阻力应对常见问题处理老方法够用 → 展示效率对比没时间学习 → 提供专门学习时间害怕被替代 →
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2526593.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!