Showing preview only (525K chars total). Download the full file or copy to clipboard to get everything.
Repository: JunManYuanLong/FunTester
Branch: okay
Commit: 87858304b866
Files: 119
Total size: 428.1 KB
Directory structure:
gitextract_d43vbshl/
├── build.gradle
├── document/
│ ├── 7788.markdown
│ ├── api.markdown
│ ├── article.markdown
│ ├── base.markdown
│ ├── directory.markdown
│ └── update.markdown
├── long/
│ ├── 1
│ ├── 30
│ ├── poster.markdown
│ └── sql/
│ ├── performance.sql
│ └── request.sql
├── readme.markdown
├── settings.gradle
└── src/
└── main/
├── groovy/
│ └── com/
│ └── funtester/
│ ├── base/
│ │ ├── bean/
│ │ │ ├── AbstractBean.groovy
│ │ │ ├── PerformanceResultBean.groovy
│ │ │ ├── RecordBean.groovy
│ │ │ ├── RequestInfo.groovy
│ │ │ ├── Result.groovy
│ │ │ └── VerifyBean.groovy
│ │ ├── constaint/
│ │ │ ├── CaseBase.java
│ │ │ ├── FixedQpsThread.java
│ │ │ ├── ThreadBase.java
│ │ │ ├── ThreadLimitTimeCount.java
│ │ │ └── ThreadLimitTimesCount.java
│ │ ├── exception/
│ │ │ ├── FailException.java
│ │ │ ├── LoginException.java
│ │ │ ├── ParamException.java
│ │ │ ├── RequestException.java
│ │ │ └── VerifyException.java
│ │ └── interfaces/
│ │ ├── IBase.java
│ │ ├── IMessage.java
│ │ ├── IMySqlBasic.java
│ │ ├── ISocketClient.java
│ │ ├── ISocketVerify.java
│ │ ├── MarkRequest.java
│ │ ├── MarkThread.java
│ │ └── ReturnCode.java
│ ├── config/
│ │ ├── Constant.java
│ │ ├── EmailConstant.java
│ │ ├── HttpClientConstant.java
│ │ ├── PropertyUtils.groovy
│ │ ├── RequestType.java
│ │ ├── SocketConstant.java
│ │ ├── SqlConstant.java
│ │ ├── SysInit.java
│ │ └── VerifyType.groovy
│ ├── db/
│ │ ├── mongodb/
│ │ │ ├── MongoBase.java
│ │ │ └── MongoObject.java
│ │ ├── mysql/
│ │ │ ├── AidThread.java
│ │ │ ├── MySqlFun.java
│ │ │ ├── MySqlObject.java
│ │ │ ├── MySqlTest.java
│ │ │ ├── SqlBase.java
│ │ │ └── TestConnectionManage.java
│ │ └── redis/
│ │ ├── RedisPool.java
│ │ └── RedisUtil.java
│ ├── dubbo/
│ │ ├── DubboBase.java
│ │ ├── DubboInvokeParams.groovy
│ │ ├── DubboParamBase.groovy
│ │ └── DubboUtil.java
│ ├── frame/
│ │ ├── JsonVerify.groovy
│ │ ├── Output.java
│ │ ├── ResponseVerify.java
│ │ ├── Save.java
│ │ ├── SourceCode.java
│ │ ├── execute/
│ │ │ ├── Concurrent.java
│ │ │ ├── ExecuteGroovy.java
│ │ │ ├── ExecuteSource.java
│ │ │ ├── FixedQpsConcurrent.java
│ │ │ ├── Progress.java
│ │ │ ├── StatisticsUtil.java
│ │ │ └── ThreadPoolUtil.groovy
│ │ └── thread/
│ │ ├── FixedQpsHeaderMark.groovy
│ │ ├── FixedQpsParamMark.java
│ │ ├── HeaderMark.java
│ │ ├── ParamMark.java
│ │ ├── QuerySqlThread.java
│ │ ├── RequestThreadTime.java
│ │ ├── RequestThreadTimes.java
│ │ ├── RequestTimeFixedQps.java
│ │ ├── RequestTimesFixedQps.java
│ │ └── UpdateSqlThread.java
│ ├── httpclient/
│ │ ├── ClientManage.java
│ │ ├── FunLibrary.java
│ │ ├── FunRequest.groovy
│ │ └── GCThread.java
│ ├── main/
│ │ ├── ExecuteMethod.java
│ │ └── PerformanceFromFile.groovy
│ ├── socket/
│ │ ├── ScoketIOFunClient.java
│ │ └── WebSocketFunClient.java
│ └── utils/
│ ├── ArgsUtil.java
│ ├── CMD.java
│ ├── CountUtil.groovy
│ ├── CurlUtil.groovy
│ ├── DecodeEncode.java
│ ├── FileUtil.groovy
│ ├── HeapDumper.java
│ ├── Join.java
│ ├── JsonUtil.groovy
│ ├── RWUtil.java
│ ├── Regex.java
│ ├── StringUtil.groovy
│ ├── Time.java
│ ├── TimeWatch.groovy
│ ├── WriteHtml.java
│ ├── message/
│ │ ├── AlertOver.java
│ │ └── EmailUtil.java
│ ├── request/
│ │ ├── Request.java
│ │ ├── RequestFile.java
│ │ └── Swagger.java
│ └── xml/
│ ├── Attr.groovy
│ ├── NodeInfo.groovy
│ ├── XMLUtil.groovy
│ └── XMLUtil2.groovy
└── resources/
├── http.properties
├── log4j2.xml
├── mysql.properties
└── redis.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
maven {url 'http://maven.aliyun.com/nexus/content/groups/public/'}
// maven { url 'http://repo1.maven.apache.org/maven2/' }
//// maven { url 'http://repo2.maven.org/maven2/' }
//// maven { url 'http://maven.oschina.net/content/groups/public/' }
// google()
}
}
plugins {
id 'java'
}
group 'funtester'
version '1.0'
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'idea'
//apply plugin: 'distribution' //打包tar包用到的插件
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
sourceCompatibility = 1.8
repositories {
mavenLocal()
maven {
url 'http://maven.aliyun.com/nexus/content/groups/public/'
}
// maven {
// url 'http://repo2.maven.org/maven2/'
// }
// maven {
// url 'http://repo1.maven.apache.org/maven2/'
// }
// maven { url 'http://maven.oschina.net/content/groups/public/' }
mavenCentral()
}
test {
useJUnitPlatform()
exclude "com/test/**"
}
sourceSets {
main {
groovy {
srcDirs 'src/main/groovy'
}
}
}
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.5'
compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.5'
compile group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.4'
// compile group: 'commons-beanutils', name: 'commons-beanutils', version: '1.8.0'
// compile group: 'commons-codec', name: 'commons-codec', version: '1.9'
// compile group: 'com.sun.jna', name: 'jna', version: '3.0.9'
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.62'
// compile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.9.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.12.1'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.12.1'
// compile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.12.1'
// compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.13'
// compile group: 'javax.mail', name: 'javax.mail-api', version: '1.6.0'
// compile group: 'com.sun.mail', name: 'javax.mail', version: '1.6.0'
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.5.7'
// compile group: 'redis.clients', name: 'jedis', version: '3.0.1'
// compile group: 'com.alibaba', name: 'dubbo', version: '2.5.3'
// compile group: 'com.jayway.jsonpath', name: 'json-path', version: '2.4.0'
// compile group: 'dom4j', name: 'dom4j', version: '1.6.1'
// compile group: 'org.java-websocket', name: 'Java-WebSocket', version: '1.5.1'
// compile group: 'io.socket', name: 'socket.io-client', version: '1.0.0'
}
jar {
from {
//添加依懒到打包文件
configurations.compile.collect {it.isDirectory() ? it : zipTree(it)}
configurations.runtime.collect {zipTree(it)}
}
manifest {
attributes 'Main-Class': 'com.funtester.main.Blog'
}
}
ext {
if (project.hasProperty('profile')) {
profile = project['profile']
} else {
profile = "FunTester"
}
println "项目环境:" + profile
}
//因为打包配置,这里会执行
task createDirs() {
doLast {
if (profile != "FunTester") {
file('build/package/lib').mkdirs()
file('build/package/bin').mkdirs()
file('build/package/logs').mkdirs()
file('build/package/conf').mkdirs()
println "文件夹创建成功!"
}
}
}
task copyFun(type: Copy) {
from('/Users/fv/Documents/workspace/funtester/build/libs')
into('/Users/fv/Library/groovy-2.5.7/lib')
println "拷贝fun.jar包到Groovy依赖成功!"
}
task copyOkay(type: Copy) {
from('/Users/fv/Documents/workspace/okay_test/target/okay_test-1.0-SNAPSHOT.jar')
into('/Users/fv/Library/groovy-2.5.7/lib')
println "拷贝okay.jar包到Groovy依赖成功!"
}
task copyJarToGroovy(dependsOn: ['copyFun', 'copyOkay']) {}
task copyLibs(type: Copy) {
doLast {
from('build/libs')
into('build/package/lib')
println "依赖拷贝成功!"
}
}
task copyConf(type: Copy) {
doLast {
from('src/main/resources/' + profile)
into('build/package/conf')
println "从src/main/resources/" + profile + "拷贝配置文件"
}
}
task copyBin(type: Copy) {
doLast {
from('src/main/resources/bin')
into('build/package/bin')
fileMode 0744//可能会失效,检查执行权限
println "依赖脚本,并设置可执行权限成功!"
}
}
// task 用来复制启动所依赖的jar包
task copyDep(type: Copy) {
doLast {
from configurations.runtime
into 'build/package/lib'
println "复制启动所依赖的jar包成功!"
}
}
//把上述的task串联起来
task prepareFile(dependsOn: [
'createDirs',
'copyLibs',
'copyConf',
'copyBin',
'copyDep'
]) {}//如果没有内容的话,可以不需要大括号
//还有一种写法表示task之间的依赖:prepareFile.dependsOn createDirs,copyLibs,copyConf,copyBin,copyDep
//指定打包的tar包的名字,以及文件来源目录
//distributions {
// monitor {
// baseName = 'azkaban-monitor'
// contents {
// from {'build/package'}
// }
// }
//}
//distribution 插件的特性
//monitorDistTar.dependsOn 'prepareFile'
//monitorDistTar.compression = Compression.NONE
//monitorDistTar.extension = 'tar'
//定义一个task,先build 然后再打包tar包
//task buildTar(dependsOn: [
// 'build',
// monitorDistTar
//]) {}
================================================
FILE: document/7788.markdown
================================================
# 7788篇
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),非著名测试开发er,欢迎关注。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 无代码合集
## 理论鸡汤
- [写给所有人的编程思维](https://mp.weixin.qq.com/s/Oj33UCnYfbUgzsBzEm2GPQ)
- [成为杰出Java开发人员的10个步骤](https://mp.weixin.qq.com/s/UCNOTSzzvTXwiUX6xpVlyA)
- [测试之《代码不朽》脑图](https://mp.weixin.qq.com/s/2aGLK3knUiiSoex-kmi0GA)
- [为什么选择软件测试作为职业道路?](https://mp.weixin.qq.com/s/o83wYvFUvy17kBPLDO609A)
- [自动化测试的障碍](https://mp.weixin.qq.com/s/ZIV7uJp7DzVoKhWOh6lvRg)
- [自动化测试的问题所在](https://mp.weixin.qq.com/s/BhvD7BnkBU8hDBsGUWok6g)
- [成为优秀自动化测试工程师的7个步骤](https://mp.weixin.qq.com/s/wdw1l4AZnPpdPBZZueCcnw)
- [优秀软件开发人员的态度](https://mp.weixin.qq.com/s/0uEEeFaR27aTlyp-sm61bA)
- [如何正确执行功能API测试](https://mp.weixin.qq.com/s/aeGx5O_jK_iTD9KUtylWmA)
- [未来10年软件测试的新趋势-上](https://mp.weixin.qq.com/s/9XgpIfXQRuKg1Pap-tfqYQ)
- [未来10年软件测试的新趋势-下](https://mp.weixin.qq.com/s/k2rZaeHoq4AX19CUzjGRVQ)
- [自动化测试解决了什么问题](https://mp.weixin.qq.com/s/96k2I_OBHayliYGs2xo6OA)
- [17种软件测试人员常用的高效技能-上](https://mp.weixin.qq.com/s/vrM_LxQMgTSdJxaPnD_CqQ)
- [17种软件测试人员常用的高效技能-下](https://mp.weixin.qq.com/s/uyWdVm74TYKb62eIRKL7nQ)
- [手动测试存在的重要原因](https://mp.weixin.qq.com/s/mW5vryoJIkeskZLkBPFe0Q)
- [编写测试用例的技巧](https://mp.weixin.qq.com/s/zZAh_XXXGOyhlm6ebzs06Q)
- [成为自动化测试的7种技能](https://mp.weixin.qq.com/s/e-HAGMO0JLR7VBBWLvk0dQ)
- [功能测试与非功能测试](https://mp.weixin.qq.com/s/oJ6PJs1zO0LOQSTRF6M6WA)
- [自动化和手动测试,保持平衡!](https://mp.weixin.qq.com/s/mMr_4C98W_FOkks2i2TiCg)
- [43种常见软件测试分类](https://mp.weixin.qq.com/s/GTMkcEm-xPtVF7_HxXGKDg)
- [自动化测试生命周期](https://mp.weixin.qq.com/s/SH-vb2RagYQ3sfCY8QM5ew)
- [代码审查如何保证软件质量](https://mp.weixin.qq.com/s/osRnG09KDqEojiV3kp2nrw)
- [TDD测试驱动开发的基础](https://mp.weixin.qq.com/s/diW_2HSbWMEsn8G6uQriOg)
- [如何在DevOps引入自动化测试](https://mp.weixin.qq.com/s/MclK3VvMN1dsiXXJO8g7ig)
- [自动化的好处](https://mp.weixin.qq.com/s/7MpWQhtozaTrlUMo1oRSBg)
- [Web端自动化测试失败原因汇总](https://mp.weixin.qq.com/s/qzFth-Q9e8MTms1M8L5TyA)
- [测试人员如何成为变革的推动者](https://mp.weixin.qq.com/s/0nTZHBOuKG0rewKAeyIqwA)
- [探索性测试为何如此重要?](https://mp.weixin.qq.com/s/nebHPfKbCO0f-G24qCh9wA)
- [5种促进业务增长的软件测试策略](https://mp.weixin.qq.com/s/3mB_DQVD2AZLPs84SmsmuA)
- [如何选择正确的自动化测试工具](https://mp.weixin.qq.com/s/_Ee78UW9CxRpV5MoTrfgCQ)
- [如何从测试自动化中实现价值](https://mp.weixin.qq.com/s/dj-sJvGjvFMYANfhIVo8jw)
- [您如何使用Selenium来计算自动化测试的投资回报率?](https://mp.weixin.qq.com/s/DVSEm0DhoAvYfTWIniabJg)
- [如何在DevOps中实施连续测试](https://mp.weixin.qq.com/s/snPXkH6WEZ2kteYP_-c5_g)
- [自动化如何选择用例](https://mp.weixin.qq.com/s/1hH5YIle4YQimJr4iGSWlA)
- [成功实施自动化测试的优点](https://mp.weixin.qq.com/s/UENdSU-NPa5AOVC9ciiy0Q)
- [测试人员常用借口](https://mp.weixin.qq.com/s/0k_Ciud2sOpRb5PPiVzECw)
- [测试自动化的边缘DevTestOps](https://mp.weixin.qq.com/s/kCySRYdCS11CA-lF30AtQA)
- [筛选自动化测试用例的技巧](https://mp.weixin.qq.com/s/SWNopZLwgpj9yYsVEHEspw)
- [什么阻碍手动测试发挥价值](https://mp.weixin.qq.com/s/t0VAVyA3ywQsHzaqzSILOw)
- [未来的QA测试工程师](https://mp.weixin.qq.com/s/ngL4sbEjZm7OFAyyWyQ3nQ)
- [Web安全检查](https://mp.weixin.qq.com/s/SewUV3GMaNKD2P7g64ctYQ)
- [关于可用性测试](https://mp.weixin.qq.com/s/aUIg40scOWzbRR89ojJWLg)
- [如何实施DevOps](https://mp.weixin.qq.com/s/UPIL942eOKR1bY0mbC-42w)
- [黑盒测试和白盒测试](https://mp.weixin.qq.com/s/5kvrYMWG0vFR3vj-aNY49g)
- [测试用例中的细节](https://mp.weixin.qq.com/s/wvScTliPwuvH9ReIoDQGNQ)
- [集成测试、单元测试、系统测试](https://mp.weixin.qq.com/s/LRkxMasRvmDYRVb0_aybtA)
- [集成测试类型和最佳实践](https://mp.weixin.qq.com/s/sSubzrs3cikLV7rmRQaWEA)
- [软件测试中质量优于数量](https://mp.weixin.qq.com/s/4FxtVFqialRz6R4680rPAw)
- [DevOps工具](https://mp.weixin.qq.com/s/4r8FoxQyYZ5naowML5Cw-Q)
- [2020年Tester自我提升](https://mp.weixin.qq.com/s/vuhUp85_6Sbg6ReAN3TTSQ)
- [DevOps中的测试工程师](https://mp.weixin.qq.com/s/42Ile_T1BAIp7QHleI-c7w)
- [敏捷团队的回归测试策略](https://mp.weixin.qq.com/s/Z7dzDdfp5_kxvzBVQ3rEDg)
- [测试自动化与自动化测试:差异很重要](https://mp.weixin.qq.com/s/6HC1bKesOs4mZYb9nOCHjw)
- [自动化新手要避免的坑(上)](https://mp.weixin.qq.com/s/MjcX40heTRhEgCFhInoqYQ)
- [自动化新手要避免的坑(下)](https://mp.weixin.qq.com/s/azDUo1IO5JgkJIS9n1CMRg)
- [如何成为全栈自动化工程师](https://mp.weixin.qq.com/s/j2rQ3COFhg939KLrgKr_bg)
- [左移测试](https://mp.weixin.qq.com/s/8zXkWV4ils17hUqlXIpXSw)
- [选择手动测试还是自动化测试?](https://mp.weixin.qq.com/s/4haRrfSIp5Plgm_GN98lRA)
- [从单元测试标准中学习](https://mp.weixin.qq.com/s/x0TyMAdPBWYL7JSPAmoQsw)
- [负载测试很重要](https://mp.weixin.qq.com/s/2q7kNQVJuNwB948ks463CA)
- [白盒测试扫盲](https://mp.weixin.qq.com/s/s_FvGZTC42GEjaWzroz1eA)
- [自动化测试项目为何失败](https://mp.weixin.qq.com/s/KFJXuLjjs1hii47C1BH8PA)
- [简化测试用例](https://mp.weixin.qq.com/s/BhwfDqhN9yoa3Iul_Eu5TA)
- [敏捷测试二三事](https://mp.weixin.qq.com/s/bKkGWJA3JhvdCjgg6-AVEQ)
- [软件测试中的虚拟化](https://mp.weixin.qq.com/s/zHyJiNFgHIo2ZaPFXsxQMg)
- [新词:QA-Ops](https://mp.weixin.qq.com/s/detcY6OVYmzOTUxfwN6CFQ)
- [生产环境中进行自动化测试](https://mp.weixin.qq.com/s/JKEGRLOlgpINUxs-6mohzA)
- [所谓UI测试](https://mp.weixin.qq.com/s/wDvUy_BhQZCSCqrlC2j1qA)
- [预上线环境失败的原因](https://mp.weixin.qq.com/s/jva0Jb2OMarERmTn7Kh2Ng)
- [自动化策略六步走](https://mp.weixin.qq.com/s/He69k8iCKhTKD1j-yV6M5g)
- [合格的测试经理必备技能](https://mp.weixin.qq.com/s/gFIYksHMn_bHEwAhmgVzjg)
- [质量保障的拓展实践](https://mp.weixin.qq.com/s/a3sd0dQnjk3TerOhfo-1ng)
- [敏捷领导者常见误区](https://mp.weixin.qq.com/s/xdq3CZflRjvDBGDLK4tNFQ)
- [功能自动化测试策略](https://mp.weixin.qq.com/s/qHmcblN4cD4JK6jT7oU4fQ)
- [性能测试、压力测试和负载测试](https://mp.weixin.qq.com/s/g26lpd7d7EtpN7pkiqkkjg)
- [如何维护自动化测试](https://mp.weixin.qq.com/s/4eh4AN_MiatMSkoCMtY3UA)
- [负载测试最佳实践](https://mp.weixin.qq.com/s/hNj7UsCCvv9TdexAcNFUvg)
- [有关UI测试计划](https://mp.weixin.qq.com/s/D0fMXwJF754a7Mr5ARY5tQ)
- [软件测试外包](https://mp.weixin.qq.com/s/sYQfb2PiQptcT0o_lLpBqQ)
- [避免PPT自动化的最佳实践](https://mp.weixin.qq.com/s/5YgYK4_YLZ1wDDhbwMTGlw)
- [如何优化软件测试成本](https://mp.weixin.qq.com/s/_eXrzDyNDA6yCRR8nPmzGA)
- [如何从手动测试转到自动化测试](https://mp.weixin.qq.com/s/EBDTX4AMnn2KTEjL88bOhQ)
- [Selenium自动化测试技巧](https://mp.weixin.qq.com/s/EzrpFaBSVITO2Y2UvYvw0w)
- [测试为何会错过Bug](https://mp.weixin.qq.com/s/UFHy8OwZjnMkB70roIS-zQ)
- [测试用例设计——一切测试的基础](https://mp.weixin.qq.com/s/0_ubnlhp2jk-jxHxJ95E9g)
- [移动应用测试:挑战,类型和最佳实践](https://mp.weixin.qq.com/s/kYxh6xki69evVDsXDxrYKQ)
- [敏捷测试中面临的挑战](https://mp.weixin.qq.com/s/vmsW56r1J7jWXHSZdcwbPg)
- [AI如何影响测试行业](https://mp.weixin.qq.com/s/d6c7u1-lAmsiIQz3UvcGKg)
- [自动测试失败的5个原因](https://mp.weixin.qq.com/s/bTakAHIcx_WyJIo-tsbvvg)
- [大促前必做的质量检查](https://mp.weixin.qq.com/s/iOku2wKnlr8pSZO0l9Q3Bw)
- [测试开发工程师工作技巧](https://mp.weixin.qq.com/s/TvrUCisja5Zbq-NIwy_2fQ)
- [敏捷回归测试](https://mp.weixin.qq.com/s/_bBQFggkZTTEqcb9R_68OA)
- [制定质量管理计划指南](https://mp.weixin.qq.com/s/ztXYE8EtwlkUdxnk1cjKVg)
- [质量管理计划的基本要素](https://mp.weixin.qq.com/s/v8lOioYn01S1F0ex4mmljA)
- [质量保障的方法和实践](https://mp.weixin.qq.com/s/hU_YCaZB-0a09dOCAVgcpw)
- [为什么测试覆盖率如此重要](https://mp.weixin.qq.com/s/0evyuiU2kdXDgMDnDKjORg)
- [自动化测试框架](https://mp.weixin.qq.com/s/vu6p_rQd3vFKDYu8JDJ0Rg)
- [敏捷中的端到端测试](https://mp.weixin.qq.com/s/cdi4xnEzDLpl9ncQguLuAQ)
- [自动化测试灵魂三问:是什么、为什么和做什么](https://mp.weixin.qq.com/s/geOejJx79-jTwafG9aXwqA)
- [基于代码的自动化和无代码自动化](https://mp.weixin.qq.com/s/8Dopihqs4XzpU-sN-I94kw)
- [物联网测试](https://mp.weixin.qq.com/s/B_JI4DANxoOq4HurxZC65Q)
- [功能测试知多少](https://mp.weixin.qq.com/s/vTxZLwlvlfIBv892Ji-oLQ)
- [如何选择自动化测试工具](https://mp.weixin.qq.com/s/yJo-d9bAZDs1Lcp8j7ISRg)
- [连续测试策略](https://mp.weixin.qq.com/s/0aD_0cUW83oPu3sl7sHNnQ)
- [如何设置质量检查流程](https://mp.weixin.qq.com/s/PQeXxMZzzU15xSfY5wkVgA)
- [编写干净的代码之变量篇](https://mp.weixin.qq.com/s/J9rGIe8a2xaLlNJq2nVmzw)
- [高效Mobile DevOps步骤](https://mp.weixin.qq.com/s/-qc-d_zJ1C9H_Uvd8gJiBw)
- [回归BUG](https://mp.weixin.qq.com/s/00j-acjPeKQ7uap62WpY3w)
- [处理回归BUG最佳实践](https://mp.weixin.qq.com/s/R3O2NruPAA2gQf4-3R6aAQ)
- [自动化测试实践清单](https://mp.weixin.qq.com/s/972WruGsYmkRroquBFoqMg)
- [自动化测试类型](https://mp.weixin.qq.com/s/GRkN8ozZiWNu21Y3KbVOBA)
- [无脚本测试](https://mp.weixin.qq.com/s/PVBxk4KEwCmWkB6mOXJFlw)
- [自动化测试转型挑战及其解决方案](https://mp.weixin.qq.com/s/BixS6jRdF5N_nvmW3_OthQ)
- [无数据驱动自动化测试](https://mp.weixin.qq.com/s/aCYRGxkzMogLbmACYo6ssw)
- [为什么自动化测试在敏捷开发中很重要](https://mp.weixin.qq.com/s/AP0wUQZ09NvSqme8e09igQ)
- [测试模型中理解压力测试和负载测试](https://mp.weixin.qq.com/s/smNLx3malzM3avkrn3EJiA)
- [移动测试工程师职业](https://mp.weixin.qq.com/s/dhtR4TbQNu5fWpmJkXGivw)
- [远程测试工作挑战](https://mp.weixin.qq.com/s/LK-GEN4OtuWVGDuG8psmOQ)
- [自动化测试用例的原子性](https://mp.weixin.qq.com/s/jA5WMHwJcu88nHXWoMBAdQ)
- [可测性经验分享](https://mp.weixin.qq.com/s/iRtUjESYS3sh3YTD-BWjdA)
- [敏捷中的回归测试的优化【译】](https://mp.weixin.qq.com/s/nDiZZgA1PIiAUCG_xwA2rA)
- [敏捷的主要优势【译】](https://mp.weixin.qq.com/s/zkI85TLI37XrPFaQ-pZYMA)
## 大咖风采
- [Tcloud 云测平台--集大成者](https://mp.weixin.qq.com/s/29sEO39_NyDiJr-kY5ufdw)
- [Android App 测试工具及知识大集合](https://mp.weixin.qq.com/s/Xk9rCW8whXOTAQuCfhZqTg)
- [Android App常规测试内容](https://mp.weixin.qq.com/s/tweeoS5wTqK3k7R2TVuDXA)
- [JVM的对象和堆](https://mp.weixin.qq.com/s/iNDpTz3gBK3By_bvUnrWOA)
# UI自动化
## UI自动化
- [自动化测试中java多线程的使用实例](https://mp.weixin.qq.com/s/BNSLaIdcTPTNj1tKpGf6fw)
- [自动化测试中递归函数的应用](https://mp.weixin.qq.com/s/86602zV9zYblhCRMiUlwdA)
## UiAutomator
- [android uiautomator一个画心形图案的方法--代码的浪漫](https://mp.weixin.qq.com/s/byfAKHxD2i83VHnuaNgIZA)
- [android UiAutomator了解源码解决控件bonds\[0,0\]无法点击](https://mp.weixin.qq.com/s/nu2ftXNUSG2_kmZjyhEcVA)
- [android UiAutomator在清除文本时遇到中文的解决办法](https://mp.weixin.qq.com/s/cNGNCoXsYBSk-MWTWLxF4g)
- [android UiAutomator获取当前页面某类控件个数的方法](https://mp.weixin.qq.com/s/njb19Sq_Kg4SusAS_eEuug)
- [android uiautomator自定义监听示例--一个弹出权限设置的监听](https://mp.weixin.qq.com/s/OKKZOf51yq6qY5D6PvN-gg)
- [如何在Mac OS上使用UiAutomator快速调试类](https://mp.weixin.qq.com/s/jm9d_42jp_BSlv-IW0BpzQ)
- [UiAutomator测试中如何恢复手机输入法](https://mp.weixin.qq.com/s/o4-zCgbdq6OsHRK9XT14QA)
- [android UiAutomator基本api的二次封装](https://mp.weixin.qq.com/s/_3jGg3ZYoeyAkjZpy8gWfQ)
- [android UiAutomator让运行失败的用例重新运行](https://mp.weixin.qq.com/s/tMOPbt1w9tRaKEuIZYKCyg)
- [利用UiAutomator写一个首页刷新的稳定性测试脚本](https://mp.weixin.qq.com/s/au9hAScsqUdcrh_usu8Pfg)
- [android UiAutomator长按实现控制按住控件时间的方法](https://mp.weixin.qq.com/s/lOvxAOMh6mmIh3CEV6Fduw)
- [android UiAutomator自定义快速调试类](https://mp.weixin.qq.com/s/iP2dTOeVkFMzU3dQ06R9GA)
- [利用UiAutomator写一个自动遍历渠道包关键功能的脚本](https://mp.weixin.qq.com/s/0vg2OlfTy0y4T6sWUG-olA)
- [android UiAutomator如何根据颜色判断控件的状态](https://mp.weixin.qq.com/s/kldsD3OZ4mJZ5yYQXfXxLw)
- [android UiAutomator控制多台手机同时运行用例的方法](https://mp.weixin.qq.com/s/z9vgpOQP0wQffmG4C_oBWg)
- [android UiAutomator使用递归函数写一个让屏幕一闪一闪提醒的方法](https://mp.weixin.qq.com/s/AzXjePdmsgs6QsICZOdPyw)
- [android UiAutomator获取视频播放进度的方法](https://mp.weixin.qq.com/s/ho070zX9rrLPmh8bZe_HgQ)
## Selenium
- [selenium2java截图保存桌面](https://mp.weixin.qq.com/s/OUfwsIo635coGONRNccYlg)
- [selenium2java调用JavaScript方法封装](https://mp.weixin.qq.com/s/t-Xs2Hr9TM2bjDiOqQX2mA)
- [selenium2java利用mysq解决向浏览器插入cookies时token过期问题](https://mp.weixin.qq.com/s/oAAkDKUGytQjxJLFkod-AQ)
- [selenium2java 遇到有三个窗口用例的处理办法](https://mp.weixin.qq.com/s/6AJBanVKYwlsNcvsu_25QQ)
- [selenium2java通过第三方登录绕过知乎登陆验证码](https://mp.weixin.qq.com/s/A5uTtxlg4l4pru2z7v1cug)
- [selenium2java使用select处理下拉框示例](https://mp.weixin.qq.com/s/FFor451WzuUzINeclGN-Ng)
- [selenium2java爬虫示例](https://mp.weixin.qq.com/s/vSZzpzEqsCtASSx6iHqxVA)
- [selenium2java写一个设置秒杀价的脚本](https://mp.weixin.qq.com/s/1ocIOYt3gdGIJrd9v2shhg)
- [selenium2java基本方法二次封装](https://mp.weixin.qq.com/s/2GaXigt13wa6JgxJkcef5g)
- [selenium2java一个弹框上传时间日期大杂烩测试用例](https://mp.weixin.qq.com/s/Z8ZeZ-zFy0q0a-e_epT1Kg)
- [selenium2java造数据例子](https://mp.weixin.qq.com/s/ACO2O5f7Po4Qn242lopMBg)
- [selenium2java让浏览器停止加载的方法](https://mp.weixin.qq.com/s/aBQdGYys3Bpyf6yigGOCIA)
- [selenium2java写一个强制刷新页面的方法](https://mp.weixin.qq.com/s/VWW7cH5WSDmw_eCabUh9LQ)
- [selenium2java通过接口获取并注入cookies](https://mp.weixin.qq.com/s/luLHWxPWSekuDMbnKsfJvg)
- [Selenium编写自动化用例的8种技巧](https://mp.weixin.qq.com/s/8wRHc_krXNfWclNeOJDNPg)
- [JUnit中用于Selenium测试的中实践](https://mp.weixin.qq.com/s/KG4sltQMCfH2MGXkRdtnwA)
- [您如何使用Selenium来计算自动化测试的投资回报率?](https://mp.weixin.qq.com/s/DVSEm0DhoAvYfTWIniabJg)
- [Selenium 4 Java的最佳测试框架](https://mp.weixin.qq.com/s/MlNyv-kb03gRTcYllxUreA)
- [Selenium 4.0 Alpha更新日志](https://mp.weixin.qq.com/s/tU7sm-pcbpRNwDU9D3OVTQ)
- [Selenium 4.0 Alpha更新实践](https://mp.weixin.qq.com/s/yT9wpO5o5aWBUus494TIHw)
- [JUnit 5和Selenium基础(一)](https://mp.weixin.qq.com/s/ehBRf7st-OxeuvI_0yW3OQ)
- [JUnit 5和Selenium基础(二)](https://mp.weixin.qq.com/s/Gt82cPmS2iX-DhKXTXiy8g)
- [JUnit 5和Selenium基础(三)](https://mp.weixin.qq.com/s/8YkonXTYgAV5-pLs9yEAVw)
- [如何在跨浏览器测试中提高效率](https://mp.weixin.qq.com/s/MB_Wv7yQ6i9BztAZtL4grA)
- [Selenium Python使用技巧(一)](https://mp.weixin.qq.com/s/39v8tXG3xig63d-ioEAi8Q)
- [Selenium Python使用技巧(二)](https://mp.weixin.qq.com/s/uDM3y9zoVjaRmJJJTNs6Vw)
- [Selenium Python使用技巧(三)](https://mp.weixin.qq.com/s/J7-CO-UDspUGSpB8isjsmQ)
- [Selenium并行测试基础](https://mp.weixin.qq.com/s/OfXipd7YtqL2AdGAQ5cIMw)
- [Selenium并行测试最佳实践](https://mp.weixin.qq.com/s/-RsQZaT5pH8DHPvm0L8Hjw)
- [维护Selenium测试自动化的最佳实践](https://mp.weixin.qq.com/s/EMD1aWuzOSfT7j3KeXhJcA)
- [Selenium自动化测试技巧](https://mp.weixin.qq.com/s/EzrpFaBSVITO2Y2UvYvw0w)
- [Selenium自动化:代码测试与无代码测试](https://mp.weixin.qq.com/s/gtmLpQ5FCeuzh1SB5mxuvg)
- [Selenium处理下拉列表](https://mp.weixin.qq.com/s/E2txSVAmDzYIEZWnyAND4g)
- [Selenium自动化常见问题](https://mp.weixin.qq.com/s/edoxu-QaD0SOw1VqrhCZWA)
- [Selenium4 IDE,它终于来了](https://mp.weixin.qq.com/s/XNotlZvFpmBmBQy1pYifOw)
- [Selenium4 IDE特性:无代码趋势和SIDE Runner](https://mp.weixin.qq.com/s/G0S9K0jHsN0P_jxdMME-cg)
- [Selenium4 IDE特性:弹性测试、循环和逻辑判断](https://mp.weixin.qq.com/s/o4_jIyi9O7s4S3CbTzl5rQ)
- [Selenium自动化最佳实践技巧(上)](https://mp.weixin.qq.com/s/lZww1azmncMMMHRY0_yKqA)
- [Selenium自动化最佳实践技巧(中)](https://mp.weixin.qq.com/s/9D0lUZ-XKHiukNeRqp6zOQ)
- [Selenium自动化最佳实践技巧(下)](https://mp.weixin.qq.com/s/opVik2ZxmTBurIBoa4yipQ)
- [Selenium等待:sleep、隐式、显式和Fluent](https://mp.weixin.qq.com/s/73BobMq9M12rYMvzxNhRtA)
- [Selenium自动化的JUnit参数化实践](https://mp.weixin.qq.com/s/WFu5rJaowxhAIcbEoEatkw)
- [Selenium异常集锦](https://mp.weixin.qq.com/s/DDkaliSVthX-c_KKG-WwNA)
- [Selenium自动化测试之前](https://mp.weixin.qq.com/s/DKjSnS9sP0SoHUw4OhOikw)
## APP性能
- [使用monkey测试时,一个控制WiFi状态的多线程类](https://mp.weixin.qq.com/s/P8HVtzHBlj_FcDAAHFKBDg)
- [java执行和停止Logcat命令及多线程实现](https://mp.weixin.qq.com/s/sUYibRc-muxQoxi48QiaRg)
- [APP性能测试中获取CPU和PSS数据多线程实现](https://mp.weixin.qq.com/s/NiJSZ8VxpdnarbDJjcJziA)
- [统计APP启动时间和进入首页时间的多线程类](https://mp.weixin.qq.com/s/IMs6vd3H-HF65Vb-zPwDhw)
- [如何获取手机性能测试数据FPS](https://mp.weixin.qq.com/s/qZy5AQkNpUXRJk46BHVzaQ)
- [一个循环启动APP并保持WiFi常开的多线程类](https://mp.weixin.qq.com/s/OgdT4IffDyAdkKmO2SS9iQ)
## 杂乱
- [测试窝,首页抄我七篇原创还拉黑,你们的良心不会痛吗?](https://mp.weixin.qq.com/s/ke5avkknkDMCLMAOGT7wiQ)
- [如何优雅地屏蔽掉Google搜索结果中视频、新闻、图片等结果](https://mp.weixin.qq.com/s/Iu7pt4Qk3w9sJp3n_UVAeQ)
- [测试玩梗--欢迎补充](https://mp.weixin.qq.com/s/y_QHbsjFCQVSCfj-A4Usmg)
- [图解HTTP脑图](https://mp.weixin.qq.com/s/100Vm8FVEuXs0x6rDGTipw)
- [测试之JVM命令脑图](https://mp.weixin.qq.com/s/qprqyv0j3SCvGw1HMjbaMQ)
- [好书推荐《Java性能权威指南》](https://mp.weixin.qq.com/s/YWd5Yx6n7887g1lMLTcsWQ)
- [2019年浏览器市场份额排行榜](https://mp.weixin.qq.com/s/4NmJ_ZCPD5UwaRCtaCfjEg)
- [JSON基础](https://mp.weixin.qq.com/s/tnQmAFfFbRloYp8J9TYurw)
- [JMeter吞吐量误差分析](https://mp.weixin.qq.com/s/jHKmFNrLmjpihnoigNNCSg)
- [JMeter如何模拟不同的网络速度](https://mp.weixin.qq.com/s/1FCwNN2htfTGF6ItdkcCzw)
- [疫情期间,如何提高远程办公效率](https://mp.weixin.qq.com/s/k_XrdqjGKMshK2Ea-VCNLw)
- [接口测试视频专题](https://mp.weixin.qq.com/s/4mKpW3QiVRee3kcVOSraog)
- [Groovy在JMeter中应用专题](https://mp.weixin.qq.com/s/KcxPUDWl7MLQemFRoIV92A)
- [Java多线程编程在JMeter中应用](https://mp.weixin.qq.com/s/xCnFx5TvIF1SAVNm-aZnxQ)
- [未来的神器fiddler Everywhere](https://mp.weixin.qq.com/s/-BSuHR6RPkdv8R-iy47MLQ)
- [Charles报错Failed to install helper解决方案](https://mp.weixin.qq.com/s/LHhMTBhlDM0DrPCvWeU0zA)
- [测试仓库推介(上)](https://mp.weixin.qq.com/s/zgy6UgNMFcbISD1NhxSAWg)
- [测试仓库推介(下)](https://mp.weixin.qq.com/s/njnpmRGoEgdxjqkR7c3a6A)
- [Fiddler Everywhere工具答疑](https://mp.weixin.qq.com/s/2peWMJ-rgDlVjs3STNeS1Q)
- [Mac上测试Internet Explorer的N种方法](https://mp.weixin.qq.com/s/HeLBPTp2dfs5IlyLMCi90Q)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [开源礼节](https://mp.weixin.qq.com/s/EyNules2f9NYdnYAX_NQSw)
- [弱网测试:最低流畅网速是多少?](https://mp.weixin.qq.com/s/rCji6fZs9yYyk7GyIWvSiA)
- [接口测试直播回顾](https://mp.weixin.qq.com/s/B8ih9sswaE-OWuVib6C16g)
- [SpotBugs报错no Groovy library is defined解决办法](https://mp.weixin.qq.com/s/XxvuVS2TmlqT5-b22vObYQ)
- [推荐好书:不要总是谦卑地弯着腰](https://mp.weixin.qq.com/s/mYNN9jSaikOF5aJEkb-Bug)
- [2020年FunTester自我总结](https://mp.weixin.qq.com/s/DeDY1JZUTk3cjjQfr3DJRg)
- [原创打油诗欣赏](https://mp.weixin.qq.com/s/3hPSDjH-3cWu6EVsjU0wOw)
- [优秀讲师 | 腾讯云+社区权威认证](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)
- [Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g)
- [假期思考题](https://mp.weixin.qq.com/s/3DOnkmYDlwk-XKg4ge3ZUw)
- [甩锅技能+1](https://mp.weixin.qq.com/s/nMwlfXZoDcRRPHcTKpvfNg)
================================================
FILE: document/api.markdown
================================================
# 接口篇
##### **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),非著名测试开发er,欢迎关注。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 接口测试
## 接口功能测试
- [开源测试服务](https://mp.weixin.qq.com/s/ZOs0cp_vt6_iiundHaKk4g)
- [使用springboot+mybatis数据库存储服务化](https://mp.weixin.qq.com/s/N_5tHW1JJLZlxCaDI2PvyQ)
- [alertover推送api的java httpclient实现实例](https://mp.weixin.qq.com/s/DJXCBEG3SbybfbT6blO1jA)
- [接口自动化通用验证类](https://mp.weixin.qq.com/s/fP1clCKkLREfg6POKV5n1A)
- [将swagger文档自动变成测试代码](https://mp.weixin.qq.com/s/SY8mVenj0zMe5b47GS9VSQ)
- [httpclient处理多用户同时在线](https://mp.weixin.qq.com/s/Nuc30Fwy6-Qyr-Pc65t1_g)
- [使用httpclient实现图灵机器人web api调用实例](https://mp.weixin.qq.com/s/dYyxvAhwSmJkNI8N9lYQfg)
- [groovy如何使用java接口测试框架发送http请求](https://mp.weixin.qq.com/s/KF5lzMT-E2IBOkp_UjuC4g)
- [httpclient调用京东万象数字营销频道新闻api实例](https://mp.weixin.qq.com/s/kSqgSbPci-q2pfsdcU5Ekw)
- [httpclient遇到socket closed解决办法](https://mp.weixin.qq.com/s/mDRC7mssKmnvcI6StQWIBQ)
- [httpclient4.5如何确保资源释放](https://mp.weixin.qq.com/s/373Lx1bv0vi-pIBgWNzC9Q)
- [httpclient如何处理302重定向](https://mp.weixin.qq.com/s/vg354AjPKhIZsnSu4GZjZg)
- [基于java的直线型接口测试框架初探](https://mp.weixin.qq.com/s/xhg4exdb1G18-nG5E7exkQ)
- [利用alertover发送获取响应失败的通知消息](https://mp.weixin.qq.com/s/w6y2UkgL3J20mAxc8fq0tA)
- [使用httpclient中EntityUtils类解析entity遇到socket closed错误的原因](https://mp.weixin.qq.com/s/RJnuOa2K6aRCElJafkFeug)
- [httpclient接口测试中重试控制器设置](https://mp.weixin.qq.com/s/hknNdq_ybQ1MoXh_dI3JVA)
- [拼接GET请求的参数](https://mp.weixin.qq.com/s/EGw_97scexH_3m2Uc8Ye5A)
- [httpclient上传文件方法的封装](https://mp.weixin.qq.com/s/HIrwl5ullvEmn_UuyLKkRg)
- [接口批量上传文件的实例](https://mp.weixin.qq.com/s/wZwkWchXXC6iddX1oVEnZQ)
- [httpclient发送https协议请求以及javax.net.ssl.SSLHandshakeException解决办法](https://mp.weixin.qq.com/s/uSHhKRrL2f9USKpSykkpkQ)
- [API测试基础](https://mp.weixin.qq.com/s/bkbUEa9CF21xMYSlhPcULw)
- [拷贝HttpRequestBase对象](https://mp.weixin.qq.com/s/kxB1c0GmSF5OAM15UQJU2Q)
- [API自动化测试指南](https://mp.weixin.qq.com/s/uy_Vn_ZVUEu3YAI1gW2T_A)
- [如何统一接口测试的功能、自动化和性能测试用例](https://mp.weixin.qq.com/s/1xqtXNVw7BdUa03nVcsMTg)
- [如何选择API测试工具](https://mp.weixin.qq.com/s/m2TNJDiqAAWYV9L6UP-29w)
- [初学者的API测试技巧](https://mp.weixin.qq.com/s/_uk6dw5Q7CfS-gXGH-TZEQ)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [多项目登录互踢测试用例](https://mp.weixin.qq.com/s/Nn_CUy_j7j6bUwHSkO0pCQ)
- [httpclient使用HTTP代理实践](https://mp.weixin.qq.com/s/24IJwJ1TJWHdfj0PzSjmvw)
- [HTTP异步连接池和多线程实践](https://mp.weixin.qq.com/s/8M348LuHakBe4GAEnnDPxw)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [socket接口开发和测试初探](https://mp.weixin.qq.com/s/uhmkbrMp91PP1pQjlEofOQ)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [基于WebSocket的client封装](https://mp.weixin.qq.com/s/1lZvsuGEa6hiRHOgOT-Kmg)
- [基于Socket.IO的Client封装](https://mp.weixin.qq.com/s/Ux90AXI9g85w7R5i3f9idg)
- [Socket.IO接口多用户测试实践](https://mp.weixin.qq.com/s/aCLaRZQs8zMK_ptJ-PjClw)
- [Python版Socket.IO接口测试脚本](https://mp.weixin.qq.com/s/oXBP6Sx3yPqlmvV9uCUScw)
- [命令行如何执行jar包里面的方法](https://mp.weixin.qq.com/s/50oMEmVEnv5Vzlm1HOxuFw)
- [JSON对象标记语法验证类](https://mp.weixin.qq.com/s/jSXmoEdMF7nWAqQuzJ5GiQ)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [无数据驱动自动化测试](https://mp.weixin.qq.com/s/aCYRGxkzMogLbmACYo6ssw)
- [白板点阵数据传输测试初探](https://mp.weixin.qq.com/s/EzFC-hIvgm7j7947TZU6BA)
- [基于Socket.IO的白板点阵坐标传输接口测试实践](https://mp.weixin.qq.com/s/pDAx4jwYvcRcdld5cKLAUw)
## 接口测试视频
- [FunTester测试框架视频讲解(序)](https://mp.weixin.qq.com/s/CJrHAAniDMyr5oDXYHpPcQ)
- [获取HTTP请求对象--测试框架视频讲解](https://mp.weixin.qq.com/s/hG89sGf96GcPb2hGnludsw)
- [发送请求和解析响应—测试框架视频解读](https://mp.weixin.qq.com/s/xUQ8o3YuZOChXZ2UGR1Kyw)
- [json对象基本操作--视频讲解](https://mp.weixin.qq.com/s/MQtcIGKwWGEMb2XD3zmAIQ)
- [GET请求实践--测试框架视频讲解](https://mp.weixin.qq.com/s/_ZEDmRPXe4SLjCgdwDtC7A)
- [POST请求实践--视频演示](https://mp.weixin.qq.com/s/g0mLzMQ4Br2e592m3p68eg)
- [如何处理header和cookie--视频演示](https://mp.weixin.qq.com/s/MkwzT9VPglSnOxY7geSUiQ)
- [FunRequest类功能--视频演示](https://mp.weixin.qq.com/s/WGS6ZwAvw7X4MC004Gz4pA)
- [接口测试业务验证--视频演示](https://mp.weixin.qq.com/s/DH8HDmaritXQnkBIFOadoA)
- [自动化测试项目基础--视频讲解](https://mp.weixin.qq.com/s/n9zu4OLyj7FbNsV0bYlOYg)
- [JSONArray基本操作--视频演示](https://mp.weixin.qq.com/s/OosDbRoknMe1riaPc3hhLg)
- [自动化项目基类实践--视频演示](https://mp.weixin.qq.com/s/IdvSi-GDtE5nqGnR-_4LWA)
- [模块类和自动化用例实践--视频演示](https://mp.weixin.qq.com/s/Y_A8M7KHmdlJJOD4B4rN4Q)
- [性能框架多线程基类和执行类--视频讲解](https://mp.weixin.qq.com/s/8Dh-5XfvX8Fm4IqmzbtY6Q)
- [定时和定量压测模式实现--视频讲解](https://mp.weixin.qq.com/s/l_4wCjVM1fAVRHgEPrcrwg)
- [基于HTTP请求的多线程实现类--视频讲解](https://mp.weixin.qq.com/s/8SG1xtzq8ArY84Bxm_SNow)
# 单元&白盒
- [Maven和Gradle中配置单元测试框架Spock](https://mp.weixin.qq.com/s/kL5keijAAZwmq_DO1NDBtw)
- [Groovy单元测试框架spock基础功能Demo](https://mp.weixin.qq.com/s/fQCyIyeQANbu2YP2ML6_8Q)
- [Groovy单元测试框架spock数据驱动Demo](https://mp.weixin.qq.com/s/uCAB7Mxt1JZW229aKp-uVQ)
- [人生苦短?试试Groovy进行单元测试](https://mp.weixin.qq.com/s/ahyP-YQTzigeq_5N8byC4g)
- [模糊断言](https://mp.weixin.qq.com/s/OlJpqHkwpY6-yyELvQ9cIw)
- [使用WireMock进行更好的集成测试](https://mp.weixin.qq.com/s/oMuVZOOQmuxSygJWH2_QHg)
- [如何测试这个方法--功能篇](https://mp.weixin.qq.com/s/4zrwkc6ccozUGjOGV563dQ)
- [如何测试这个方法--性能篇](https://mp.weixin.qq.com/s/QXl9_9Bj5c191oxkXmByUA)
- [单元测试用例](https://mp.weixin.qq.com/s/UFEXJ1aXOvJUYp49iVLr5w)
- [关于测试覆盖率](https://mp.weixin.qq.com/s/E15D785fkaWH7-YhiE5gPw)
- [JUnit 5和Selenium基础(一)](https://mp.weixin.qq.com/s/ehBRf7st-OxeuvI_0yW3OQ)
- [JUnit 5和Selenium基础(二)](https://mp.weixin.qq.com/s/Gt82cPmS2iX-DhKXTXiy8g)
- [JUnit 5和Selenium基础(三)](https://mp.weixin.qq.com/s/8YkonXTYgAV5-pLs9yEAVw)
- [浅谈单元测试](https://mp.weixin.qq.com/s/mJM9qXQepSYQ9vLBnBEs3Q)
- [Spock 2.0 M1版本初探](https://mp.weixin.qq.com/s/nyYh2QzER03kIk-w9P9GNw)
- [Java并发BUG基础篇](https://mp.weixin.qq.com/s/NR4vYx81HtgAEqH2Q93k2Q)
- [Java并发BUG提升篇](https://mp.weixin.qq.com/s/GCRRe8hJpe1QJtxq9VBEhg)
- [集成测试、单元测试、系统测试](https://mp.weixin.qq.com/s/LRkxMasRvmDYRVb0_aybtA)
- [从单元测试标准中学习](https://mp.weixin.qq.com/s/x0TyMAdPBWYL7JSPAmoQsw)
- [白盒测试扫盲](https://mp.weixin.qq.com/s/s_FvGZTC42GEjaWzroz1eA)
- [Mock System.in和检查System.out](https://mp.weixin.qq.com/s/1ly3uXCZsukmIylN6F5GxQ)
- [单元测试框架spock和Mockito应用](https://mp.weixin.qq.com/s/s21Lts1UnG9HwOEVvgj-uw)
- [Mockito框架Mock Void方法](https://mp.weixin.qq.com/s/R95wOMVyeDCHm3_Z0S2kqg)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [Intellij静态代码扫描插件SpotBugs](https://mp.weixin.qq.com/s/8ivsMNOmT0LDfvcM06IGMg)
- [SpotBugs注解SuppressWarnings在Java&Groovy中的应用](https://mp.weixin.qq.com/s/R0JoqmAqhUbRSjIJ61h_tg)
## 性能测试
- [Linux性能监控软件netdata中文汉化版](https://mp.weixin.qq.com/s/7VG7gHx7FUvsuNtBTJpjWA)
- [性能测试框架](https://mp.weixin.qq.com/s/3_09j7-5ex35u30HQRyWug)
- [性能测试框架第二版](https://mp.weixin.qq.com/s/JPyGQ2DRC6EVBmZkxAoVWA)
- [性能测试框架第三版](https://mp.weixin.qq.com/s/Mk3PoH7oJX7baFmbeLtl_w)
- [一个时间计数器timewatch辅助性能测试](https://mp.weixin.qq.com/s/-YZ04n2kyfO0q2QaKHX_0Q)
- [如何在Linux命令行界面愉快进行性能测试](https://mp.weixin.qq.com/s/fwGqBe1SpA2V0lPfAOd04Q)
- [Mac+httpclient高并发配置实例](https://mp.weixin.qq.com/s/r4a-vGz0pxeZBPPH3phujw)
- [单点登录性能测试方案](https://mp.weixin.qq.com/s/sv8FnvIq44dFEq63LpOD2A)
- [如何对消息队列做性能测试](https://mp.weixin.qq.com/s/MNt22aW3Op9VQ5OoMzPwBw)
- [如何对修改密码接口进行压测](https://mp.weixin.qq.com/s/9CL_6-uZOlAh7oeo7NOpag)
- [如何对单行多次update接口进行压测](https://mp.weixin.qq.com/s/Ly1Y4iPGgL6FNRsbOTv0sg)
- [如何对多行单次update接口进行压测](https://mp.weixin.qq.com/s/Fsqw7vlw6K9EKa_XJwGIgQ)
- [如何获取JVM堆转储文件](https://mp.weixin.qq.com/s/qCg7nsXVvT1q-9yquQOfWA)
- [性能测试中标记每个请求](https://mp.weixin.qq.com/s/PokvzoLdVf_y9inlVXHJHQ)
- [如何对N个接口按比例压测](https://mp.weixin.qq.com/s/GZxbH4GjDkk4BLqnUj1_kw)
- [如何性能测试中进行业务验证](https://mp.weixin.qq.com/s/OEvRy1bS2Yq_w1kGiidmng)
- [性能测试中记录每一个耗时请求](https://mp.weixin.qq.com/s/VXcp4uIMm8mRgqe8fVhuCQ)
- [线程安全类在性能测试中应用](https://mp.weixin.qq.com/s/0-Y63wXqIugVC8RiKldHvg)
- [利用微基准测试修正压测结果](https://mp.weixin.qq.com/s/dmO33qhOBrTByw_NshS-uA)
- [性能测试如何减少本机误差](https://mp.weixin.qq.com/s/S6b_wwSowVolp1Uu6sEIOA)
- [服务端性能优化之异步查询转同步](https://mp.weixin.qq.com/s/okYP2aOPfkWj2FjZcAtQNA)
- [服务端性能优化之双重检查锁](https://mp.weixin.qq.com/s/-bOyHBcqFlJY3c0PEZaWgQ)
- [多种登录方式定量性能测试方案](https://mp.weixin.qq.com/s/WuZ2h2rr0rNBgEvQVioacA)
- [性能测试中图形化输出测试数据](https://mp.weixin.qq.com/s/EMvpYIsszdwBJFPIxztTvA)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [手机号验证码登录性能测试](https://mp.weixin.qq.com/s/i-j8fJAdcsJ7v8XPOnPDAw)
- [绑定手机号性能测试](https://mp.weixin.qq.com/s/K5x1t1dKtIT2NKV6k4v5mw)
- [终止性能测试并输出报告](https://mp.weixin.qq.com/s/II4-UbKDikctmS_vRT-xLg)
- [CountDownLatch类在性能测试中应用](https://mp.weixin.qq.com/s/uYBPPOjauR2h81l2uKMANQ)
- [CyclicBarrier类在性能测试中应用](https://mp.weixin.qq.com/s/kvEHX3t_2xpMke9vwOdWrg)
- [Phaser类在性能测试中应用](https://mp.weixin.qq.com/s/plxNnQq7yNQvHYEGpyY4uA)
- [如何同时压测创建和删除接口](https://mp.weixin.qq.com/s/NCeoEF3DkEtpdaaQ365I0Q)
- [固定QPS压测模式探索](https://mp.weixin.qq.com/s/S2h-zEUoik_CWs60RL6g7Q)
- [固定QPS压测初试](https://mp.weixin.qq.com/s/ySlJmDIH3fFB4qEnL-ueMg)
- [命令行如何执行jar包里面的方法](https://mp.weixin.qq.com/s/50oMEmVEnv5Vzlm1HOxuFw)
- [链路压测中如何记录每一个耗时的请求](https://mp.weixin.qq.com/s/8sb5QZcKbBjTxCaXK5ajXA)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [性能测试中集合点和多阶段同步问题初探](https://mp.weixin.qq.com/s/NlpD1WyMrcG1V5RYfY0Plg)
- [性能测试中标记请求参数实践](https://mp.weixin.qq.com/s/2FNMU-k_En26FCqWkYpvhQ)
- [测试模型中理解压力测试和负载测试](https://mp.weixin.qq.com/s/smNLx3malzM3avkrn3EJiA)
- [重放浏览器单个请求性能测试实践](https://mp.weixin.qq.com/s/a10hxCrIzS4TV9JwmDSI3Q)
- [重放浏览器多个请求性能测试实践](https://mp.weixin.qq.com/s/Hm1Kpp1PMrZ5rYFW8l2GlA)
- [重放浏览器请求多链路性能测试实践](https://mp.weixin.qq.com/s/9YSBLAyHVw8Z6IfK-nJTpQ)
- [性能测试中异步展示测试进度](https://mp.weixin.qq.com/s/AOERJbEc4ATJqhjvnxgQoA)
- [ThreadLocal在链路性能测试中实践](https://mp.weixin.qq.com/s/3qhNdHHSStELzNraQSpcew)
- [Socket接口固定QPS性能测试实践](https://mp.weixin.qq.com/s/I9-14L8THxvtX1NJY0KPfw)
- [单链路性能测试实践](https://mp.weixin.qq.com/s/4xHLP-GZwrNu5cFKdfsB6g)
- [链路性能测试中参数多样性方法分享](https://mp.weixin.qq.com/s/I1pm0fulNrj_S-YkNz-gEA)
- [链路测试中参数流转图](https://mp.weixin.qq.com/s/xyo9HXBLoXgLW6MSFH3V6w)
- [线程同步类CyclicBarrier在性能测试集合点应用](https://mp.weixin.qq.com/s/K2YySxX9T4v_rzbvIbIHJA)
================================================
FILE: document/article.markdown
================================================
# 总目录
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),[GDevOps官方合作媒体](https://mp.weixin.qq.com/s/OnwvRqrgXq_pGp8eOXOTgw),非著名测试开发er。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 接口测试
## 接口功能测试
- [开源测试服务](https://mp.weixin.qq.com/s/ZOs0cp_vt6_iiundHaKk4g)
- [使用springboot+mybatis数据库存储服务化](https://mp.weixin.qq.com/s/N_5tHW1JJLZlxCaDI2PvyQ)
- [alertover推送api的java httpclient实现实例](https://mp.weixin.qq.com/s/DJXCBEG3SbybfbT6blO1jA)
- [接口自动化通用验证类](https://mp.weixin.qq.com/s/fP1clCKkLREfg6POKV5n1A)
- [将swagger文档自动变成测试代码](https://mp.weixin.qq.com/s/SY8mVenj0zMe5b47GS9VSQ)
- [httpclient处理多用户同时在线](https://mp.weixin.qq.com/s/Nuc30Fwy6-Qyr-Pc65t1_g)
- [使用httpclient实现图灵机器人web api调用实例](https://mp.weixin.qq.com/s/dYyxvAhwSmJkNI8N9lYQfg)
- [groovy如何使用java接口测试框架发送http请求](https://mp.weixin.qq.com/s/KF5lzMT-E2IBOkp_UjuC4g)
- [httpclient调用京东万象数字营销频道新闻api实例](https://mp.weixin.qq.com/s/kSqgSbPci-q2pfsdcU5Ekw)
- [httpclient遇到socket closed解决办法](https://mp.weixin.qq.com/s/mDRC7mssKmnvcI6StQWIBQ)
- [httpclient4.5如何确保资源释放](https://mp.weixin.qq.com/s/373Lx1bv0vi-pIBgWNzC9Q)
- [httpclient如何处理302重定向](https://mp.weixin.qq.com/s/vg354AjPKhIZsnSu4GZjZg)
- [基于java的直线型接口测试框架初探](https://mp.weixin.qq.com/s/xhg4exdb1G18-nG5E7exkQ)
- [利用alertover发送获取响应失败的通知消息](https://mp.weixin.qq.com/s/w6y2UkgL3J20mAxc8fq0tA)
- [使用httpclient中EntityUtils类解析entity遇到socket closed错误的原因](https://mp.weixin.qq.com/s/RJnuOa2K6aRCElJafkFeug)
- [httpclient接口测试中重试控制器设置](https://mp.weixin.qq.com/s/hknNdq_ybQ1MoXh_dI3JVA)
- [拼接GET请求的参数](https://mp.weixin.qq.com/s/EGw_97scexH_3m2Uc8Ye5A)
- [httpclient上传文件方法的封装](https://mp.weixin.qq.com/s/HIrwl5ullvEmn_UuyLKkRg)
- [接口批量上传文件的实例](https://mp.weixin.qq.com/s/wZwkWchXXC6iddX1oVEnZQ)
- [httpclient发送https协议请求以及javax.net.ssl.SSLHandshakeException解决办法](https://mp.weixin.qq.com/s/uSHhKRrL2f9USKpSykkpkQ)
- [API测试基础](https://mp.weixin.qq.com/s/bkbUEa9CF21xMYSlhPcULw)
- [拷贝HttpRequestBase对象](https://mp.weixin.qq.com/s/kxB1c0GmSF5OAM15UQJU2Q)
- [API自动化测试指南](https://mp.weixin.qq.com/s/uy_Vn_ZVUEu3YAI1gW2T_A)
- [如何统一接口测试的功能、自动化和性能测试用例](https://mp.weixin.qq.com/s/1xqtXNVw7BdUa03nVcsMTg)
- [如何选择API测试工具](https://mp.weixin.qq.com/s/m2TNJDiqAAWYV9L6UP-29w)
- [初学者的API测试技巧](https://mp.weixin.qq.com/s/_uk6dw5Q7CfS-gXGH-TZEQ)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [多项目登录互踢测试用例](https://mp.weixin.qq.com/s/Nn_CUy_j7j6bUwHSkO0pCQ)
- [httpclient使用HTTP代理实践](https://mp.weixin.qq.com/s/24IJwJ1TJWHdfj0PzSjmvw)
- [HTTP异步连接池和多线程实践](https://mp.weixin.qq.com/s/8M348LuHakBe4GAEnnDPxw)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [socket接口开发和测试初探](https://mp.weixin.qq.com/s/uhmkbrMp91PP1pQjlEofOQ)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [基于WebSocket的client封装](https://mp.weixin.qq.com/s/1lZvsuGEa6hiRHOgOT-Kmg)
- [基于Socket.IO的Client封装](https://mp.weixin.qq.com/s/Ux90AXI9g85w7R5i3f9idg)
- [Socket.IO接口多用户测试实践](https://mp.weixin.qq.com/s/aCLaRZQs8zMK_ptJ-PjClw)
- [Python版Socket.IO接口测试脚本](https://mp.weixin.qq.com/s/oXBP6Sx3yPqlmvV9uCUScw)
- [命令行如何执行jar包里面的方法](https://mp.weixin.qq.com/s/50oMEmVEnv5Vzlm1HOxuFw)
- [JSON对象标记语法验证类](https://mp.weixin.qq.com/s/jSXmoEdMF7nWAqQuzJ5GiQ)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [无数据驱动自动化测试](https://mp.weixin.qq.com/s/aCYRGxkzMogLbmACYo6ssw)
- [白板点阵数据传输测试初探](https://mp.weixin.qq.com/s/EzFC-hIvgm7j7947TZU6BA)
- [基于Socket.IO的白板点阵坐标传输接口测试实践](https://mp.weixin.qq.com/s/pDAx4jwYvcRcdld5cKLAUw)
## 接口测试视频
- [FunTester测试框架视频讲解(序)](https://mp.weixin.qq.com/s/CJrHAAniDMyr5oDXYHpPcQ)
- [获取HTTP请求对象--测试框架视频讲解](https://mp.weixin.qq.com/s/hG89sGf96GcPb2hGnludsw)
- [发送请求和解析响应—测试框架视频解读](https://mp.weixin.qq.com/s/xUQ8o3YuZOChXZ2UGR1Kyw)
- [json对象基本操作--视频讲解](https://mp.weixin.qq.com/s/MQtcIGKwWGEMb2XD3zmAIQ)
- [GET请求实践--测试框架视频讲解](https://mp.weixin.qq.com/s/_ZEDmRPXe4SLjCgdwDtC7A)
- [POST请求实践--视频演示](https://mp.weixin.qq.com/s/g0mLzMQ4Br2e592m3p68eg)
- [如何处理header和cookie--视频演示](https://mp.weixin.qq.com/s/MkwzT9VPglSnOxY7geSUiQ)
- [FunRequest类功能--视频演示](https://mp.weixin.qq.com/s/WGS6ZwAvw7X4MC004Gz4pA)
- [接口测试业务验证--视频演示](https://mp.weixin.qq.com/s/DH8HDmaritXQnkBIFOadoA)
- [自动化测试项目基础--视频讲解](https://mp.weixin.qq.com/s/n9zu4OLyj7FbNsV0bYlOYg)
- [JSONArray基本操作--视频演示](https://mp.weixin.qq.com/s/OosDbRoknMe1riaPc3hhLg)
- [自动化项目基类实践--视频演示](https://mp.weixin.qq.com/s/IdvSi-GDtE5nqGnR-_4LWA)
- [模块类和自动化用例实践--视频演示](https://mp.weixin.qq.com/s/Y_A8M7KHmdlJJOD4B4rN4Q)
- [性能框架多线程基类和执行类--视频讲解](https://mp.weixin.qq.com/s/8Dh-5XfvX8Fm4IqmzbtY6Q)
- [定时和定量压测模式实现--视频讲解](https://mp.weixin.qq.com/s/l_4wCjVM1fAVRHgEPrcrwg)
- [基于HTTP请求的多线程实现类--视频讲解](https://mp.weixin.qq.com/s/8SG1xtzq8ArY84Bxm_SNow)
# 单元&白盒
- [Maven和Gradle中配置单元测试框架Spock](https://mp.weixin.qq.com/s/kL5keijAAZwmq_DO1NDBtw)
- [Groovy单元测试框架spock基础功能Demo](https://mp.weixin.qq.com/s/fQCyIyeQANbu2YP2ML6_8Q)
- [Groovy单元测试框架spock数据驱动Demo](https://mp.weixin.qq.com/s/uCAB7Mxt1JZW229aKp-uVQ)
- [人生苦短?试试Groovy进行单元测试](https://mp.weixin.qq.com/s/ahyP-YQTzigeq_5N8byC4g)
- [模糊断言](https://mp.weixin.qq.com/s/OlJpqHkwpY6-yyELvQ9cIw)
- [使用WireMock进行更好的集成测试](https://mp.weixin.qq.com/s/oMuVZOOQmuxSygJWH2_QHg)
- [如何测试这个方法--功能篇](https://mp.weixin.qq.com/s/4zrwkc6ccozUGjOGV563dQ)
- [如何测试这个方法--性能篇](https://mp.weixin.qq.com/s/QXl9_9Bj5c191oxkXmByUA)
- [单元测试用例](https://mp.weixin.qq.com/s/UFEXJ1aXOvJUYp49iVLr5w)
- [关于测试覆盖率](https://mp.weixin.qq.com/s/E15D785fkaWH7-YhiE5gPw)
- [JUnit 5和Selenium基础(一)](https://mp.weixin.qq.com/s/ehBRf7st-OxeuvI_0yW3OQ)
- [JUnit 5和Selenium基础(二)](https://mp.weixin.qq.com/s/Gt82cPmS2iX-DhKXTXiy8g)
- [JUnit 5和Selenium基础(三)](https://mp.weixin.qq.com/s/8YkonXTYgAV5-pLs9yEAVw)
- [浅谈单元测试](https://mp.weixin.qq.com/s/mJM9qXQepSYQ9vLBnBEs3Q)
- [Spock 2.0 M1版本初探](https://mp.weixin.qq.com/s/nyYh2QzER03kIk-w9P9GNw)
- [Java并发BUG基础篇](https://mp.weixin.qq.com/s/NR4vYx81HtgAEqH2Q93k2Q)
- [Java并发BUG提升篇](https://mp.weixin.qq.com/s/GCRRe8hJpe1QJtxq9VBEhg)
- [集成测试、单元测试、系统测试](https://mp.weixin.qq.com/s/LRkxMasRvmDYRVb0_aybtA)
- [从单元测试标准中学习](https://mp.weixin.qq.com/s/x0TyMAdPBWYL7JSPAmoQsw)
- [白盒测试扫盲](https://mp.weixin.qq.com/s/s_FvGZTC42GEjaWzroz1eA)
- [Mock System.in和检查System.out](https://mp.weixin.qq.com/s/1ly3uXCZsukmIylN6F5GxQ)
- [单元测试框架spock和Mockito应用](https://mp.weixin.qq.com/s/s21Lts1UnG9HwOEVvgj-uw)
- [Mockito框架Mock Void方法](https://mp.weixin.qq.com/s/R95wOMVyeDCHm3_Z0S2kqg)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [Intellij静态代码扫描插件SpotBugs](https://mp.weixin.qq.com/s/8ivsMNOmT0LDfvcM06IGMg)
- [SpotBugs注解SuppressWarnings在Java&Groovy中的应用](https://mp.weixin.qq.com/s/R0JoqmAqhUbRSjIJ61h_tg)
## 性能测试
- [Linux性能监控软件netdata中文汉化版](https://mp.weixin.qq.com/s/7VG7gHx7FUvsuNtBTJpjWA)
- [性能测试框架](https://mp.weixin.qq.com/s/3_09j7-5ex35u30HQRyWug)
- [性能测试框架第二版](https://mp.weixin.qq.com/s/JPyGQ2DRC6EVBmZkxAoVWA)
- [性能测试框架第三版](https://mp.weixin.qq.com/s/Mk3PoH7oJX7baFmbeLtl_w)
- [一个时间计数器timewatch辅助性能测试](https://mp.weixin.qq.com/s/-YZ04n2kyfO0q2QaKHX_0Q)
- [如何在Linux命令行界面愉快进行性能测试](https://mp.weixin.qq.com/s/fwGqBe1SpA2V0lPfAOd04Q)
- [Mac+httpclient高并发配置实例](https://mp.weixin.qq.com/s/r4a-vGz0pxeZBPPH3phujw)
- [单点登录性能测试方案](https://mp.weixin.qq.com/s/sv8FnvIq44dFEq63LpOD2A)
- [如何对消息队列做性能测试](https://mp.weixin.qq.com/s/MNt22aW3Op9VQ5OoMzPwBw)
- [如何对修改密码接口进行压测](https://mp.weixin.qq.com/s/9CL_6-uZOlAh7oeo7NOpag)
- [如何对单行多次update接口进行压测](https://mp.weixin.qq.com/s/Ly1Y4iPGgL6FNRsbOTv0sg)
- [如何对多行单次update接口进行压测](https://mp.weixin.qq.com/s/Fsqw7vlw6K9EKa_XJwGIgQ)
- [如何获取JVM堆转储文件](https://mp.weixin.qq.com/s/qCg7nsXVvT1q-9yquQOfWA)
- [性能测试中标记每个请求](https://mp.weixin.qq.com/s/PokvzoLdVf_y9inlVXHJHQ)
- [如何对N个接口按比例压测](https://mp.weixin.qq.com/s/GZxbH4GjDkk4BLqnUj1_kw)
- [如何性能测试中进行业务验证](https://mp.weixin.qq.com/s/OEvRy1bS2Yq_w1kGiidmng)
- [性能测试中记录每一个耗时请求](https://mp.weixin.qq.com/s/VXcp4uIMm8mRgqe8fVhuCQ)
- [线程安全类在性能测试中应用](https://mp.weixin.qq.com/s/0-Y63wXqIugVC8RiKldHvg)
- [利用微基准测试修正压测结果](https://mp.weixin.qq.com/s/dmO33qhOBrTByw_NshS-uA)
- [性能测试如何减少本机误差](https://mp.weixin.qq.com/s/S6b_wwSowVolp1Uu6sEIOA)
- [服务端性能优化之异步查询转同步](https://mp.weixin.qq.com/s/okYP2aOPfkWj2FjZcAtQNA)
- [服务端性能优化之双重检查锁](https://mp.weixin.qq.com/s/-bOyHBcqFlJY3c0PEZaWgQ)
- [多种登录方式定量性能测试方案](https://mp.weixin.qq.com/s/WuZ2h2rr0rNBgEvQVioacA)
- [性能测试中图形化输出测试数据](https://mp.weixin.qq.com/s/EMvpYIsszdwBJFPIxztTvA)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [手机号验证码登录性能测试](https://mp.weixin.qq.com/s/i-j8fJAdcsJ7v8XPOnPDAw)
- [绑定手机号性能测试](https://mp.weixin.qq.com/s/K5x1t1dKtIT2NKV6k4v5mw)
- [终止性能测试并输出报告](https://mp.weixin.qq.com/s/II4-UbKDikctmS_vRT-xLg)
- [CountDownLatch类在性能测试中应用](https://mp.weixin.qq.com/s/uYBPPOjauR2h81l2uKMANQ)
- [CyclicBarrier类在性能测试中应用](https://mp.weixin.qq.com/s/kvEHX3t_2xpMke9vwOdWrg)
- [Phaser类在性能测试中应用](https://mp.weixin.qq.com/s/plxNnQq7yNQvHYEGpyY4uA)
- [如何同时压测创建和删除接口](https://mp.weixin.qq.com/s/NCeoEF3DkEtpdaaQ365I0Q)
- [固定QPS压测模式探索](https://mp.weixin.qq.com/s/S2h-zEUoik_CWs60RL6g7Q)
- [固定QPS压测初试](https://mp.weixin.qq.com/s/ySlJmDIH3fFB4qEnL-ueMg)
- [命令行如何执行jar包里面的方法](https://mp.weixin.qq.com/s/50oMEmVEnv5Vzlm1HOxuFw)
- [链路压测中如何记录每一个耗时的请求](https://mp.weixin.qq.com/s/8sb5QZcKbBjTxCaXK5ajXA)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [性能测试中集合点和多阶段同步问题初探](https://mp.weixin.qq.com/s/NlpD1WyMrcG1V5RYfY0Plg)
- [性能测试中标记请求参数实践](https://mp.weixin.qq.com/s/2FNMU-k_En26FCqWkYpvhQ)
- [测试模型中理解压力测试和负载测试](https://mp.weixin.qq.com/s/smNLx3malzM3avkrn3EJiA)
- [重放浏览器单个请求性能测试实践](https://mp.weixin.qq.com/s/a10hxCrIzS4TV9JwmDSI3Q)
- [重放浏览器多个请求性能测试实践](https://mp.weixin.qq.com/s/Hm1Kpp1PMrZ5rYFW8l2GlA)
- [重放浏览器请求多链路性能测试实践](https://mp.weixin.qq.com/s/9YSBLAyHVw8Z6IfK-nJTpQ)
- [性能测试中异步展示测试进度](https://mp.weixin.qq.com/s/AOERJbEc4ATJqhjvnxgQoA)
- [ThreadLocal在链路性能测试中实践](https://mp.weixin.qq.com/s/3qhNdHHSStELzNraQSpcew)
- [Socket接口固定QPS性能测试实践](https://mp.weixin.qq.com/s/I9-14L8THxvtX1NJY0KPfw)
- [单链路性能测试实践](https://mp.weixin.qq.com/s/4xHLP-GZwrNu5cFKdfsB6g)
- [链路性能测试中参数多样性方法分享](https://mp.weixin.qq.com/s/I1pm0fulNrj_S-YkNz-gEA)
- [链路测试中参数流转图](https://mp.weixin.qq.com/s/xyo9HXBLoXgLW6MSFH3V6w)
- [线程同步类CyclicBarrier在性能测试集合点应用](https://mp.weixin.qq.com/s/K2YySxX9T4v_rzbvIbIHJA)
- [链路压测中各接口性能统计](https://mp.weixin.qq.com/s/Deyop0mMpHrRWj9JTHzayw)
- [性能测试框架中QPS取样器实现](https://mp.weixin.qq.com/s/4-5WhwwE1oRQ7cMUDv7J2w)
- [链路压测中的支路问题初探](https://mp.weixin.qq.com/s/9iN9XndRPH4vIgc0-jVpUA)
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),[GDevOps官方合作媒体](https://mp.weixin.qq.com/s/OnwvRqrgXq_pGp8eOXOTgw),非著名测试开发er。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 语言合集
## Java
- [java一行代码打印心形](https://mp.weixin.qq.com/s/QPSryoSbViVURpSa9QXtpg)
- [操作的原子性与线程安全](https://mp.weixin.qq.com/s/QU3llkGLepX2VCch8Y9GKw)
- [快看,i++真的不安全](https://mp.weixin.qq.com/s/-CdWdROKSEq_ZiLX2kWxzA)
- [原子操作组合与线程安全](https://mp.weixin.qq.com/s/XB5LXucAF5Bo8EkfLZYRmw)
- [java利用for循环输出正三角新解](https://mp.weixin.qq.com/s/nnMR2177LLVn4u_9s9Fl4g)
- [在main方法之前,到底执行了什么?](https://mp.weixin.qq.com/s/jWxiCMfwmvRHrjPdRG8ZyQ)
- [传参传的到底是什么?](https://mp.weixin.qq.com/s/p_pEQwE6h6q7PprkW-kjbg)
- [json里面put了null会怎么样?](https://mp.weixin.qq.com/s/gQVROe01I3JzIqNdTSHpDQ)
- [主线程都结束了,为何进程还在执行](https://mp.weixin.qq.com/s/q2v5JU5dtmNEol7I7IVY-Q)
- [java测试框架如何执行groovy脚本文件](https://mp.weixin.qq.com/s/0GYt1l3_z5-1qzBNl6_PzA)
- [java用递归筛选法求N以内的孪生质数(孪生素数)](https://mp.weixin.qq.com/s/PSdCb-DrgMPb4WpJJMexmQ)
- [从JVM堆内存分析验证深浅拷贝](https://mp.weixin.qq.com/s/SdYDnoau1rjjvPC2SUymBg)
- [如何学习Java基础](https://mp.weixin.qq.com/s/FCPStkYoJF67NYln4Lc6xg)
- [如何保存HTTPrequestbase和CloseableHttpResponse](https://mp.weixin.qq.com/s/gRY8HRQHCh0PfyS7Q22IwA)
- [如何在匿名thread子类中保证线程安全](https://mp.weixin.qq.com/s/GCXx_-ummi0JfZQ7GTIxig)
- [Java服务端两个常见的并发错误](https://mp.weixin.qq.com/s/5VvCox3eY6sQDsuaKB4ZIw)
- [Java中interface属性和实例方法](https://mp.weixin.qq.com/s/vrKkM6522tgw3v_cL7R8HA)
- [服务端性能优化之双重检查锁](https://mp.weixin.qq.com/s/-bOyHBcqFlJY3c0PEZaWgQ)
- [Java并发BUG基础篇](https://mp.weixin.qq.com/s/NR4vYx81HtgAEqH2Q93k2Q)
- [Java并发BUG提升篇](https://mp.weixin.qq.com/s/GCRRe8hJpe1QJtxq9VBEhg)
- [性能测试中图形化输出测试数据](https://mp.weixin.qq.com/s/EMvpYIsszdwBJFPIxztTvA)
- [超大对象导致Full GC超高的BUG分享](https://mp.weixin.qq.com/s/L15-0JW9WK-E005GeOG9WQ)
- [利用ThreadLocal解决线程同步问题](https://mp.weixin.qq.com/s/VEm8jt3ZUEUdyyeXPC8VvQ)
- [线程安全集合类中的对象是安全的么?](https://mp.weixin.qq.com/s/WKSuPEfzZCVwjVTcoD0Dyg)
- [如何使用“dd MM”解析日期](https://mp.weixin.qq.com/s/v9ooAj3dKu53JXgxB482HA)
- [Java和Groovy正则使用](https://mp.weixin.qq.com/s/DT3BKE3ZcCKf6TLzGc5wbg)
- [运行越来越快的Java热点代码](https://mp.weixin.qq.com/s/AP0BcDEjDuaouaB0RXJOoQ)
- [6个重要的JVM性能参数](https://mp.weixin.qq.com/s/b1QnapiAVn0HD5DQU9JrIw)
- [ArrayList浅、深拷贝](https://mp.weixin.qq.com/s/kYsBzFsCyDPUssdV3MDqLA)
- [Java性能测试中两种锁的实现](https://mp.weixin.qq.com/s/j9dGFvYzCJ0AGwYUtTrTsw)
- [测试如何处理Java异常](https://mp.weixin.qq.com/s/H00GWiATOD8QHJu3UewrBw)
- [创建Java守护线程](https://mp.weixin.qq.com/s/_UjWdvq8QWYTshr4SeniBg)
- [Lambda表达式在线程安全Map中应用](https://mp.weixin.qq.com/s/zZjB5aOWh4a_k1eoEsR5ww)
- [Java程序是如何浪费内存的](https://mp.weixin.qq.com/s/w7VF5m5cc0X7LNvqmwGfvg)
- [Java中的自定义异常](https://mp.weixin.qq.com/s/nspIdxFP9qEDtagGN4gaMQ)
- [Java文本块](https://mp.weixin.qq.com/s/GwasvpJsd7uLngvCr6KlQw)
- [CountDownLatch类在性能测试中应用](https://mp.weixin.qq.com/s/uYBPPOjauR2h81l2uKMANQ)
- [CyclicBarrier类在性能测试中应用](https://mp.weixin.qq.com/s/kvEHX3t_2xpMke9vwOdWrg)
- [Phaser类在性能测试中应用](https://mp.weixin.qq.com/s/plxNnQq7yNQvHYEGpyY4uA)
- [Java压缩/解压缩字符串](https://mp.weixin.qq.com/s/7vHNd5dEN93DPUqgS8od_A)
- [Java删除空字符:Java8 & Java11](https://mp.weixin.qq.com/s/6dlgYgTFZsHuJ4Eaby5eyg)
- [Java Stream中map和flatMap方法](https://mp.weixin.qq.com/s/0FG2o7VUAG6z8a_0je-1EQ)
- [泛型类的正确用法](https://mp.weixin.qq.com/s/1azilraonPIZNCnw_9MB5Q)
- [Java字符串到数组的转换--最后放大招](https://mp.weixin.qq.com/s/iMUYZYkJ5CjykwWqinNm5g)
- [Java求数组的并集--最后放大招](https://mp.weixin.qq.com/s/bZ93SGakyiRbaRujhx4nvw)
- [Java计算数组平均值--最后放大招](https://mp.weixin.qq.com/s/dxQaFHu2PyAbOK6jpEgEUQ)
- [Math.abs()求绝对值返回负值BUG分享](https://mp.weixin.qq.com/s/RHzExuRqF1XsBtzGKzmgGA)
- [Java代理模式初探](https://mp.weixin.qq.com/s/SBL_K2PQez3vDHhtAN9NLg)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [性能测试中异步展示测试进度](https://mp.weixin.qq.com/s/AOERJbEc4ATJqhjvnxgQoA)
- [Java中的ThreadLocal功能演示](https://mp.weixin.qq.com/s/n92k1JswHKrqT7Y_CD9Q0w)
- [ThreadLocal在链路性能测试中实践](https://mp.weixin.qq.com/s/3qhNdHHSStELzNraQSpcew)
- [歪解字符串中连续出现次数最多问题](https://mp.weixin.qq.com/s/xBy4iB4qLd4WQgCsVVuemw)
- [Java&Groovy下载文件对比](https://mp.weixin.qq.com/s/T9WUynej2yOZhCkDUhaLYw)
- [线程同步类CyclicBarrier在性能测试集合点应用](https://mp.weixin.qq.com/s/K2YySxX9T4v_rzbvIbIHJA)
- [Java线程同步三剑客](https://mp.weixin.qq.com/s/cAmd11-HdwXNU3tp4TiDLg)
## Groovy
- [java和groovy混合编程时提示找不到符合错误解决办法](https://mp.weixin.qq.com/s/dLC2W7nIi5zCuK6JTkiA-w)
- [groovy使用stream语法递归筛选法求N以内的质数](https://mp.weixin.qq.com/s/TsrVn1cuQUrU6wj9OnR-FQ)
- [使用Groovy进行Bash(shell)操作](https://mp.weixin.qq.com/s/fgCTlZUF3QeNj6jzq1ZgGg)
- [使用Groovy和Gradle轻松进行数据库操作](https://mp.weixin.qq.com/s/lwmclrnW0csykVRhu7dNTQ)
- [愉快地使用Groovy Shell](https://mp.weixin.qq.com/s/fJh7fbB3naBFBEiaS62oxw)
- [Gradle+Groovy基础篇](https://mp.weixin.qq.com/s/c2j7G-PoNtAB3oYYDUhCGw)
- [Gradle+Groovy提高篇](https://mp.weixin.qq.com/s/yXmYj_1fynLkR0-5FV_Arw)
- [Groovy重载操作符](https://mp.weixin.qq.com/s/4jW06Q4_vjFR9DovRTTuHg)
- [用Groovy处理JMeter断言和日志](https://mp.weixin.qq.com/s/Q4yPA4p8dZYAARZ60ZDh9w)
- [用Groovy处理JMeter变量](https://mp.weixin.qq.com/s/BxtweLrBUptM8r3LxmeM_Q)
- [用Groovy在JMeter中执行命令行](https://mp.weixin.qq.com/s/VTip7tiLpwBOr1gUoZ0n8A)
- [用Groovy处理JMeter中的请求参数](https://mp.weixin.qq.com/s/9pCUOXWpMwXR5ynvCMYJ7A)
- [Java和Groovy正则使用](https://mp.weixin.qq.com/s/DT3BKE3ZcCKf6TLzGc5wbg)
- [Groovy中的元组](https://mp.weixin.qq.com/s/0-ka0-tv1vyKbiA6m44jRw)
- [从Java到Groovy的八级进化论](https://mp.weixin.qq.com/s/QTrRHsD3w-zLGbn79y8yUg)
- [用Groovy在JMeter中使用正则提取赋值](https://mp.weixin.qq.com/s/9riPpnQZCfKGscuzOOpYmQ)
- [Groovy在JMeter中处理cookie](https://mp.weixin.qq.com/s/DCnDjWaj2aiKv5HVw3-n6A)
- [Groovy在JMeter中处理header](https://mp.weixin.qq.com/s/juY-1jEWODJ5HHiEsxhIEw)
- [Groovy的神奇NullObject](https://mp.weixin.qq.com/s/jLGisN_30PrCgNP33Sww0g)
- [Groovy中的list](https://mp.weixin.qq.com/s/0mUe1_WrUiEm1t6kqCV3eQ)
- [JMeter参数签名——Groovy脚本形式](https://mp.weixin.qq.com/s/wQN9-xAUQofSqiAVFXdqug)
- [Groovy中的闭包](https://mp.weixin.qq.com/s/pfcG47gSPfUveAaEfdeo8A)
- [JMeter参数签名——Groovy工具类形式](https://mp.weixin.qq.com/s/urwU4p9ofv9sU-JFy5Z0iA)
- [删除List中null的N种方法--最后放大招](https://mp.weixin.qq.com/s/4mfskN781dybyL59dbSbeQ)
- [混合Java函数和Groovy闭包](https://mp.weixin.qq.com/s/FAIzGgLSX2u7RKbOGs3lGA)
- [Groovy重载操作符(终极版)](https://mp.weixin.qq.com/s/4oYGJ2B2Y1AqxsIj8v5nZA)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [Groovy小记it关键字和IDE报错](https://mp.weixin.qq.com/s/cIMHzkvKtH0a0ewkiBnV8g)
- [JsonPath验证类既Groovy重载操作符实践](https://mp.weixin.qq.com/s/5gc04CAsBY6pWxe5c2P41w)
- [Groovy枚举类初始化异常分析](https://mp.weixin.qq.com/s/koFhpBZM1MFYYxCNxUKPyQ)
- [Java&Groovy下载文件对比](https://mp.weixin.qq.com/s/T9WUynej2yOZhCkDUhaLYw)
## Python
- [python使用filter方法递归筛选法求N以内的质数(素数)--附一行打印心形标记的代码解析](https://mp.weixin.qq.com/s/D8RfpdIi8smCL8TAzBcNpA)
- [关于python版微信使用经验分享](https://mp.weixin.qq.com/s/19IaI6ETZAm_T4ePPlXqIg)
- [python用递归筛选法求N以内的孪生质数(孪生素数)](https://mp.weixin.qq.com/s/rVY2pTl8So11WCvA9GrFbA)
- [利用python wxpy和requests写一个自动应答微信机器人实例](https://mp.weixin.qq.com/s/Fni2kX5BRjdqOQ-glCLjRg)
- [Python版Socket.IO接口测试脚本](https://mp.weixin.qq.com/s/oXBP6Sx3yPqlmvV9uCUScw)
## 测开笔记
- [我的开发日记(一)](https://mp.weixin.qq.com/s/eQgpOKbXsU9vOmxp0Xiklg)
- [我的开发日记(二)](https://mp.weixin.qq.com/s/XuffL3ZmKKOgHDtH_cEYOw)
- [我的开发日记(三)](https://mp.weixin.qq.com/s/a-I0agh6nWp8RLlcmbgf5w)
- [我的开发日记(四)](https://mp.weixin.qq.com/s/QukXd00Mx_dbkgiXys0FNg)
- [我的开发日记(五)](https://mp.weixin.qq.com/s/6P3nScsVW6MfMcyIqcA1AQ)
- [我的开发日记(六)](https://mp.weixin.qq.com/s/Gz2QmukONNldSy9Fd29u5w)
- [我的开发日记(七)](https://mp.weixin.qq.com/s/MjZ-nFXfQkHMsXS0fX1c1w)
- [我的开发日记(八)](https://mp.weixin.qq.com/s/6ZhNcFm-gR5dhKQjEkE3Rg)
- [我的开发日记(九)](https://mp.weixin.qq.com/s/VfD2T3orojGxnylr3Q5UeA)
- [我的开发日记(十)](https://mp.weixin.qq.com/s/6DWth40LGbAraJi05G16Pw)
- [我的开发日记(十一)](https://mp.weixin.qq.com/s/nsX5A-P6QbePHDN_Pse0_A)
- [我的开发日记(十二)](https://mp.weixin.qq.com/s/XA1KJXBP3Zl-XFswXxUtvg)
- [我的开发日记(十三)](https://mp.weixin.qq.com/s/_QPUu5pUlg4A_AlC5wOGkA)
- [我的开发日记(十四)](https://mp.weixin.qq.com/s/Qy1YKAb3wqW_Ip2FwH7Otw)
- [我的开发日记(十五)](https://mp.weixin.qq.com/s/bwkvz2t6YItQD0O_BIxpHQ)
- [这些年,我写过的BUG(一)](https://mp.weixin.qq.com/s/mVTmT1FdwWl1e0BaL7Ne1g)
- [这些年,我写过的BUG(二)](https://mp.weixin.qq.com/s/NMz5n0ZMf6taGb-gr1BLyw)
- [FunTester测试框架架构图初探](https://mp.weixin.qq.com/s/bcMbVDkWbHSXjZFDeFyJsQ)
- [FunTester测试项目架构图初探](https://mp.weixin.qq.com/s/wqb8FXRbEXrhDuZounmNXA)
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),[GDevOps官方合作媒体](https://mp.weixin.qq.com/s/OnwvRqrgXq_pGp8eOXOTgw),非著名测试开发er。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 案例分享
## 测试方案
- [如何对消息队列做性能测试](https://mp.weixin.qq.com/s/MNt22aW3Op9VQ5OoMzPwBw)
- [如何对修改密码接口进行压测](https://mp.weixin.qq.com/s/9CL_6-uZOlAh7oeo7NOpag)
- [如何测试概率型业务接口](https://mp.weixin.qq.com/s/kUVffhjae3eYivrGqo6ZMg)
- [如何测试非固定型概率算法P=p(1+0.1*N)](https://mp.weixin.qq.com/s/sgg8v-Bi-_sUDJXwuTCMGg)
- [性能测试中标记每个请求](https://mp.weixin.qq.com/s/PokvzoLdVf_y9inlVXHJHQ)
- [如何对N个接口按比例压测](https://mp.weixin.qq.com/s/GZxbH4GjDkk4BLqnUj1_kw)
- [多种登录方式定量性能测试方案](https://mp.weixin.qq.com/s/WuZ2h2rr0rNBgEvQVioacA)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [绑定手机号性能测试](https://mp.weixin.qq.com/s/K5x1t1dKtIT2NKV6k4v5mw)
- [手机号验证码登录性能测试](https://mp.weixin.qq.com/s/i-j8fJAdcsJ7v8XPOnPDAw)
- [重放浏览器单个请求性能测试实践](https://mp.weixin.qq.com/s/a10hxCrIzS4TV9JwmDSI3Q)
- [重放浏览器多个请求性能测试实践](https://mp.weixin.qq.com/s/Hm1Kpp1PMrZ5rYFW8l2GlA)
- [重放浏览器请求多链路性能测试实践](https://mp.weixin.qq.com/s/9YSBLAyHVw8Z6IfK-nJTpQ)
- [Socket接口固定QPS性能测试实践](https://mp.weixin.qq.com/s/I9-14L8THxvtX1NJY0KPfw)
## BUG集锦
- [一个MySQL索引引发的血案](https://mp.weixin.qq.com/s/KLSber-gPg53JVfsCa3Dtw)
- [微软Zune闰年BUG分析](https://mp.weixin.qq.com/s/zpqAUcNcHaZjWUdUYH_loQ)
- [“双花”BUG的测试分享](https://mp.weixin.qq.com/s/0dsBsssNfg-seJ_tu9zFaQ)
- [iOS 11计算器1+2+3=24真的是bug么?](https://mp.weixin.qq.com/s/nokQhe_Hqcq-o7pZJmFlqQ)
- [不要在遍历的时候删除](https://mp.weixin.qq.com/s/MIczbEpbOrADL0_V7ZUhlg)
- [连开100年会员会怎样](https://mp.weixin.qq.com/s/mZw-SFIxFFbE-o8UeXhdfg)
- [异步查询转同步加redis业务实现的BUG分享](https://mp.weixin.qq.com/s/ni3f6QTxw0K-0I3epvEYOA)
- [Java服务端两个常见的并发错误](https://mp.weixin.qq.com/s/5VvCox3eY6sQDsuaKB4ZIw)
- [超大对象导致Full GC超高的BUG分享](https://mp.weixin.qq.com/s/L15-0JW9WK-E005GeOG9WQ)
- [访问权限导致toString返回空BUG分享](https://mp.weixin.qq.com/s/usDOcuJrXOmEKN-mVBzRKg)
- [异常使用中的BUG](https://mp.weixin.qq.com/s/IG9Ar3IT7CrlSv4d0lCvgA)
- [Math.abs()求绝对值返回负值BUG分享](https://mp.weixin.qq.com/s/RHzExuRqF1XsBtzGKzmgGA)
## 爬虫实践
- [接口爬虫之网页表单数据提取](https://mp.weixin.qq.com/s/imJ5u67xhYQaEzv-O1in4g)
- [httpclient爬虫爬取汉字拼音等信息](https://mp.weixin.qq.com/s/w-IvBxAsotmPA3pydpIo1w)
- [httpclient爬虫爬取电影信息和下载地址实例](https://mp.weixin.qq.com/s/TB49X4S-ioyoW5CrzAnHcw)
- [httpclient 多线程爬虫实例](https://mp.weixin.qq.com/s/nXL-MP4Y6CN2hgZQefWEeQ)
- [groovy爬虫练习之——企业信息](https://mp.weixin.qq.com/s/1TisDceIL1-Luqz_wOqAiw)
- [httpclient 爬虫实例——爬取三级中学名](https://mp.weixin.qq.com/s/Dd7U30aHYauqBFxJdxaiyg)
- [电子书网站爬虫实践](https://mp.weixin.qq.com/s/KGW0dIS5NTLgxyhSjxDiOw)
- [groovy爬虫实例——历史上的今天](https://mp.weixin.qq.com/s/5LDUvpU6t_GZ09uhZr224A)
- [爬取720万条城市历史天气数据](https://mp.weixin.qq.com/s/vOyKpeGlJSJp9bQ8NIMe2A)
- [记一次失败的爬虫](https://mp.weixin.qq.com/s/SMylrZLXDGw5f1xKI9ObnA)
- [爬虫实践--CBA历年比赛数据](https://mp.weixin.qq.com/s/mM_QSQddabU5im_O6iVR-Q)
- [图片爬虫实践](https://mp.weixin.qq.com/s/u5bRSyKsmn3TcjqEEqRJpw)
# 工具合集
## JSON合集
- [JsonPath实践(一)](https://mp.weixin.qq.com/s/Cq0_v_ptbGd4f5y8HIsq7w)
- [JsonPath实践(二)](https://mp.weixin.qq.com/s/w_iJTiuQahIw6U00CJVJZg)
- [JsonPath实践(三)](https://mp.weixin.qq.com/s/58A3k0T6dbOkBJ5nRYKDqA)
- [JsonPath实践(四)](https://mp.weixin.qq.com/s/8ER61qrkMj8bdBpyuq9r6w)
- [JsonPath实践(五)](https://mp.weixin.qq.com/s/knVLW960WXnckGLstdrOVQ)
- [JsonPath实践(六)](https://mp.weixin.qq.com/s/ckBCK3t1w68FLBhaw5a7Jw)
- [JsonPath工具类封装](https://mp.weixin.qq.com/s/KyuCuG5fVEExxBdGJO2LdA)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [JsonPath验证类既Groovy重载操作符实践](https://mp.weixin.qq.com/s/5gc04CAsBY6pWxe5c2P41w)
- [JSON对象标记语法验证类](https://mp.weixin.qq.com/s/jSXmoEdMF7nWAqQuzJ5GiQ)
- [使用jq处理JSON数据(一)](https://mp.weixin.qq.com/s/45-ztTx2scbNY5u7NQzeIA)
## Jacoco覆盖率
- [接口测试代码覆盖率(jacoco)方案分享](https://mp.weixin.qq.com/s/D73Sq6NLjeRKN8aCpGLOjQ)
- [jacoco无法读取build.xml配置中源码路径解决办法](https://mp.weixin.qq.com/s/8_x0rVfkIi-uX3y0drx_jw)
- [使用JaCoCo Maven插件创建代码覆盖率报告](https://mp.weixin.qq.com/s/4Jo05k2WxytiSSNW9WTV-A)
- [Java 8,Jenkins,Jacoco和Sonar进行持续集成](https://mp.weixin.qq.com/s/dOoXnKnWtQmmC5itClsl4g)
- [jacoco测试覆盖率过滤非业务类](https://mp.weixin.qq.com/s/7YGe9pCHw3wd87tgOlKjSA)
## arthas诊断工具
- [arthas快速入门视频演示](https://mp.weixin.qq.com/s/Wl5QMD52isGTRuAP4Cpo-A)
- [arthas进阶thread命令视频演示](https://mp.weixin.qq.com/s/XuF7Nr1sGC3diIn50zlDDQ)
- [arthas命令jvm,sysprop,sysenv,vmoption视频演示](https://mp.weixin.qq.com/s/87BsTYqnTCnVdG3a_kBcng)
- [arthas命令logger动态修改日志级别--视频演示](https://mp.weixin.qq.com/s/w724P9B12eTC9rMbavwsMA)
- [arthas命令sc和sm视频演示](https://mp.weixin.qq.com/s/Ga63sjW_bOKQqfnA5LTb9w)
- [arthas命令ognl视频演示](https://mp.weixin.qq.com/s/cMCaXFwjp6QHFq40TvP4bQ)
- [arthas命令redefine实现Java热更新](https://mp.weixin.qq.com/s/2HUXfJhoUfg4yMzSoRHK9w)
- [arthas命令monitor监控方法执行](https://mp.weixin.qq.com/s/7-oe3UoTY8bzpi89tIKvQQ)
- [arthas命令watch观察方法调用(上)](https://mp.weixin.qq.com/s/6fMKP7H4Q7ll_0v-wyN19g)
- [arthas命令watch观察方法调用(下)](https://mp.weixin.qq.com/s/-r2kufxdOjRb2TgF2HPskg)
- [arthas命令trace追踪方法链路](https://mp.weixin.qq.com/s/bzkdKZugkOl8C-_xTw92YA)
- [arthas命令tt方法时空隧道](https://mp.weixin.qq.com/s/mDczYmVdSmL5ZbK7bb8i0A)
## moco API
- [解决moco框架API在post请求json参数情况下query失效的问题](https://mp.weixin.qq.com/s/V5lXoepEBtPJrSUHA0Uz5A)
- [给moco API添加limit功能](https://mp.weixin.qq.com/s/pXJECi15ieNLmA0uIqEqfA)
- [给moco API添加random功能](https://mp.weixin.qq.com/s/YTcbFbFaWB5arW_fubgTTQ)
- [解决moco框架API在cycle方法缺失的问题](https://mp.weixin.qq.com/s/YfsPa7eW8WV65CDbPooBPg)
- [五行代码构建静态博客](https://mp.weixin.qq.com/s/hZnimJOg5OqxRSDyFvuiiQ)
- [moco API模拟框架视频讲解(上)](https://mp.weixin.qq.com/s/X5-fFXe018_O60WCRdawZg)
- [moco API模拟框架视频讲解(中)](https://mp.weixin.qq.com/s/g2En-9W9JWYrCLQr_WPEBA)
- [moco API模拟框架视频讲解(下)](https://mp.weixin.qq.com/s/mz__DiNxMGHwIKCLsjKR8g)
- [如何mock固定QPS的接口](https://mp.weixin.qq.com/s/yogj9Fni0KJkyQuKuDYlbA)
- [mock延迟响应的接口](https://mp.weixin.qq.com/s/x_fu0InQpYIUJIQFi9a50g)
- [moco固定QPS接口升级补偿机制](https://mp.weixin.qq.com/s/zAM91e_REo4edSPTLuHLOw)
## 工具类
- [java网格输出的类](https://mp.weixin.qq.com/s/BJTJu0LGjn7Hc9J1yT04KQ)
- [java使用poi写入excel文档的一种解决方案](https://mp.weixin.qq.com/s/Ft56gd1B9CPrQs2zq4Cpug)
- [java使用poi读取excel文档的一种解决方案](https://mp.weixin.qq.com/s/ltZGx9J7E8DTer0D-pfQ2Q)
- [MongoDB操作类封装](https://mp.weixin.qq.com/s/u-RHOE5XrjOEkelWIxdplw)
- [java网格输出的类](https://mp.weixin.qq.com/s/QW8nKM2Bz7C75fdkCzSbpw)
- [将json数据格式化输出到控制台](https://mp.weixin.qq.com/s/2IPwvh-33Ov2jBh0_L8shA)
- [利用反射根据方法名执行方法的使用示例](https://mp.weixin.qq.com/s/5ntwDo4ZVcTh1PmK4vkNfA)
- [解决统计出现次数问题的方法类](https://mp.weixin.qq.com/s/gqz4wuKkMWAOIQwMtiupnA)
- [java利用时间戳来获取UTC时间](https://mp.weixin.qq.com/s/wbDIrwDnxb9_XWkkmP3A_g)
- [如何遍历执行一个包里面每个类的用例方法](https://mp.weixin.qq.com/s/OJwCOHCJ4TalatsEWbtzIQ)
- [阿拉伯数字转成汉字](https://mp.weixin.qq.com/s/jNZXIvwMpdxt7jIAlVBgHg)
- [获取JVM转储文件的Java工具类](https://mp.weixin.qq.com/s/f_TlOb3m8MeR3argBmTzzA)
- [基于DOM的XML文件解析类](https://mp.weixin.qq.com/s/scRj7OAhvJYL3mx_hCFp4A)
- [XML文件解析实践(DOM解析)](https://mp.weixin.qq.com/s/V2DG3osaPNUJzFNDQgqM-w)
- [基于DOM4J的XML文件解析类](https://mp.weixin.qq.com/s/K5R7iMXouTn4g0p14T7iAQ)
- [将HTTP请求对象转成curl命令行](https://mp.weixin.qq.com/s/861uMAMMWtINjy4Z99WA6w)
## 构建工具
- [java和groovy混编的Maven项目如何用intellij打包执行jar包](https://mp.weixin.qq.com/s/bKexZXlONeo3r6FDhfMltQ)
- [window系统权限不足导致gradle构建失败的解决办法](https://mp.weixin.qq.com/s/dqiQvmVG1o6glU-pknLDwQ)
- [使用groovy脚本使gradle灵活加载本地jar包的两种方式](https://mp.weixin.qq.com/s/p3K3ZS7iOUeKO7E94gKFVg)
- [Java 8,Jenkins,Jacoco和Sonar进行持续集成](https://mp.weixin.qq.com/s/dOoXnKnWtQmmC5itClsl4g)
- [Gradle如何在任务失败后继续构建](https://mp.weixin.qq.com/s/GcXDzRN7cM_QQpt9ytqoKg)
- [Gradle+Groovy基础篇](https://mp.weixin.qq.com/s/c2j7G-PoNtAB3oYYDUhCGw)
- [Gradle+Groovy提高篇](https://mp.weixin.qq.com/s/yXmYj_1fynLkR0-5FV_Arw)
- [Maven进行增量构建](https://mp.weixin.qq.com/s/ThQ7j6TS93KJZFqlNx8IQg)
- [SonarQube8.3中的Maven项目的测试覆盖率报告](https://mp.weixin.qq.com/s/Xhp26jyE1c7Auielz48Llw)
## plotly可视化
- [MacOS使用pip安装pandas提示Cannot uninstall 'numpy'解决方案](https://mp.weixin.qq.com/s/fIqMAMXRQvf_vBtS5jDsyg)
- [Python使用plotly生成本地文件教程](https://mp.weixin.qq.com/s/4dJdIP-g3fF40vX7S31jNg)
- [Python2.7使用plotly绘制本地散点图和折线图实例](https://mp.weixin.qq.com/s/9QWrA0c-STmrmjSkBYWvbQ)
- [Python可视化工具plotly从数据库读取数据作图示例](https://mp.weixin.qq.com/s/EUtPidiz_r1rpQBH_kudbA)
- [利用Python+plotly制作接口请求时间的violin图表](https://mp.weixin.qq.com/s/3GdiLaiVRfkxwM3MOG-U8w)
- [Python+plotly生成本地饼状图实例](https://mp.weixin.qq.com/s/61Qz9Kz-4ruzC0OvIuElpA)
- [python plotly处理接口性能测试数据方法封装](https://mp.weixin.qq.com/s/NxVdvYlD7PheNCv8AMYqhg)
- [利用python+plotly 制作接口响应时间Distplot图表](https://mp.weixin.qq.com/s/yrcUW1fFC18newqHcxhVvw)
- [利用 python+plotly 制作Contour Plots模拟双波源干涉现象](https://mp.weixin.qq.com/s/vNW80BDeHsyjNQrnaBGk3Q)
- [利用 python+plotly 制作双波源干涉三维图像](https://mp.weixin.qq.com/s/KSeV8VvQXRIg-bnzYoa5qg)
- [python plotly制作接口响应耗时的时间序列表(Time Series )](https://mp.weixin.qq.com/s/U8chcVzCjGTdT3T_X5v4kw)
- [python使用plotly批量生成图表](https://mp.weixin.qq.com/s/l18WfWz-s6qQ1JKKuh_2AQ)
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),[GDevOps官方合作媒体](https://mp.weixin.qq.com/s/OnwvRqrgXq_pGp8eOXOTgw),非著名测试开发er。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 无代码合集
## 理论鸡汤
- [写给所有人的编程思维](https://mp.weixin.qq.com/s/Oj33UCnYfbUgzsBzEm2GPQ)
- [成为杰出Java开发人员的10个步骤](https://mp.weixin.qq.com/s/UCNOTSzzvTXwiUX6xpVlyA)
- [测试之《代码不朽》脑图](https://mp.weixin.qq.com/s/2aGLK3knUiiSoex-kmi0GA)
- [为什么选择软件测试作为职业道路?](https://mp.weixin.qq.com/s/o83wYvFUvy17kBPLDO609A)
- [自动化测试的障碍](https://mp.weixin.qq.com/s/ZIV7uJp7DzVoKhWOh6lvRg)
- [自动化测试的问题所在](https://mp.weixin.qq.com/s/BhvD7BnkBU8hDBsGUWok6g)
- [成为优秀自动化测试工程师的7个步骤](https://mp.weixin.qq.com/s/wdw1l4AZnPpdPBZZueCcnw)
- [优秀软件开发人员的态度](https://mp.weixin.qq.com/s/0uEEeFaR27aTlyp-sm61bA)
- [如何正确执行功能API测试](https://mp.weixin.qq.com/s/aeGx5O_jK_iTD9KUtylWmA)
- [未来10年软件测试的新趋势-上](https://mp.weixin.qq.com/s/9XgpIfXQRuKg1Pap-tfqYQ)
- [未来10年软件测试的新趋势-下](https://mp.weixin.qq.com/s/k2rZaeHoq4AX19CUzjGRVQ)
- [自动化测试解决了什么问题](https://mp.weixin.qq.com/s/96k2I_OBHayliYGs2xo6OA)
- [17种软件测试人员常用的高效技能-上](https://mp.weixin.qq.com/s/vrM_LxQMgTSdJxaPnD_CqQ)
- [17种软件测试人员常用的高效技能-下](https://mp.weixin.qq.com/s/uyWdVm74TYKb62eIRKL7nQ)
- [手动测试存在的重要原因](https://mp.weixin.qq.com/s/mW5vryoJIkeskZLkBPFe0Q)
- [编写测试用例的技巧](https://mp.weixin.qq.com/s/zZAh_XXXGOyhlm6ebzs06Q)
- [成为自动化测试的7种技能](https://mp.weixin.qq.com/s/e-HAGMO0JLR7VBBWLvk0dQ)
- [功能测试与非功能测试](https://mp.weixin.qq.com/s/oJ6PJs1zO0LOQSTRF6M6WA)
- [自动化和手动测试,保持平衡!](https://mp.weixin.qq.com/s/mMr_4C98W_FOkks2i2TiCg)
- [43种常见软件测试分类](https://mp.weixin.qq.com/s/GTMkcEm-xPtVF7_HxXGKDg)
- [自动化测试生命周期](https://mp.weixin.qq.com/s/SH-vb2RagYQ3sfCY8QM5ew)
- [代码审查如何保证软件质量](https://mp.weixin.qq.com/s/osRnG09KDqEojiV3kp2nrw)
- [TDD测试驱动开发的基础](https://mp.weixin.qq.com/s/diW_2HSbWMEsn8G6uQriOg)
- [如何在DevOps引入自动化测试](https://mp.weixin.qq.com/s/MclK3VvMN1dsiXXJO8g7ig)
- [自动化的好处](https://mp.weixin.qq.com/s/7MpWQhtozaTrlUMo1oRSBg)
- [Web端自动化测试失败原因汇总](https://mp.weixin.qq.com/s/qzFth-Q9e8MTms1M8L5TyA)
- [测试人员如何成为变革的推动者](https://mp.weixin.qq.com/s/0nTZHBOuKG0rewKAeyIqwA)
- [探索性测试为何如此重要?](https://mp.weixin.qq.com/s/nebHPfKbCO0f-G24qCh9wA)
- [5种促进业务增长的软件测试策略](https://mp.weixin.qq.com/s/3mB_DQVD2AZLPs84SmsmuA)
- [如何选择正确的自动化测试工具](https://mp.weixin.qq.com/s/_Ee78UW9CxRpV5MoTrfgCQ)
- [如何从测试自动化中实现价值](https://mp.weixin.qq.com/s/dj-sJvGjvFMYANfhIVo8jw)
- [您如何使用Selenium来计算自动化测试的投资回报率?](https://mp.weixin.qq.com/s/DVSEm0DhoAvYfTWIniabJg)
- [如何在DevOps中实施连续测试](https://mp.weixin.qq.com/s/snPXkH6WEZ2kteYP_-c5_g)
- [自动化如何选择用例](https://mp.weixin.qq.com/s/1hH5YIle4YQimJr4iGSWlA)
- [成功实施自动化测试的优点](https://mp.weixin.qq.com/s/UENdSU-NPa5AOVC9ciiy0Q)
- [测试人员常用借口](https://mp.weixin.qq.com/s/0k_Ciud2sOpRb5PPiVzECw)
- [测试自动化的边缘DevTestOps](https://mp.weixin.qq.com/s/kCySRYdCS11CA-lF30AtQA)
- [筛选自动化测试用例的技巧](https://mp.weixin.qq.com/s/SWNopZLwgpj9yYsVEHEspw)
- [什么阻碍手动测试发挥价值](https://mp.weixin.qq.com/s/t0VAVyA3ywQsHzaqzSILOw)
- [未来的QA测试工程师](https://mp.weixin.qq.com/s/ngL4sbEjZm7OFAyyWyQ3nQ)
- [Web安全检查](https://mp.weixin.qq.com/s/SewUV3GMaNKD2P7g64ctYQ)
- [关于可用性测试](https://mp.weixin.qq.com/s/aUIg40scOWzbRR89ojJWLg)
- [如何实施DevOps](https://mp.weixin.qq.com/s/UPIL942eOKR1bY0mbC-42w)
- [黑盒测试和白盒测试](https://mp.weixin.qq.com/s/5kvrYMWG0vFR3vj-aNY49g)
- [测试用例中的细节](https://mp.weixin.qq.com/s/wvScTliPwuvH9ReIoDQGNQ)
- [集成测试、单元测试、系统测试](https://mp.weixin.qq.com/s/LRkxMasRvmDYRVb0_aybtA)
- [集成测试类型和最佳实践](https://mp.weixin.qq.com/s/sSubzrs3cikLV7rmRQaWEA)
- [软件测试中质量优于数量](https://mp.weixin.qq.com/s/4FxtVFqialRz6R4680rPAw)
- [DevOps工具](https://mp.weixin.qq.com/s/4r8FoxQyYZ5naowML5Cw-Q)
- [2020年Tester自我提升](https://mp.weixin.qq.com/s/vuhUp85_6Sbg6ReAN3TTSQ)
- [DevOps中的测试工程师](https://mp.weixin.qq.com/s/42Ile_T1BAIp7QHleI-c7w)
- [敏捷团队的回归测试策略](https://mp.weixin.qq.com/s/Z7dzDdfp5_kxvzBVQ3rEDg)
- [测试自动化与自动化测试:差异很重要](https://mp.weixin.qq.com/s/6HC1bKesOs4mZYb9nOCHjw)
- [自动化新手要避免的坑(上)](https://mp.weixin.qq.com/s/MjcX40heTRhEgCFhInoqYQ)
- [自动化新手要避免的坑(下)](https://mp.weixin.qq.com/s/azDUo1IO5JgkJIS9n1CMRg)
- [如何成为全栈自动化工程师](https://mp.weixin.qq.com/s/j2rQ3COFhg939KLrgKr_bg)
- [左移测试](https://mp.weixin.qq.com/s/8zXkWV4ils17hUqlXIpXSw)
- [选择手动测试还是自动化测试?](https://mp.weixin.qq.com/s/4haRrfSIp5Plgm_GN98lRA)
- [从单元测试标准中学习](https://mp.weixin.qq.com/s/x0TyMAdPBWYL7JSPAmoQsw)
- [负载测试很重要](https://mp.weixin.qq.com/s/2q7kNQVJuNwB948ks463CA)
- [白盒测试扫盲](https://mp.weixin.qq.com/s/s_FvGZTC42GEjaWzroz1eA)
- [自动化测试项目为何失败](https://mp.weixin.qq.com/s/KFJXuLjjs1hii47C1BH8PA)
- [简化测试用例](https://mp.weixin.qq.com/s/BhwfDqhN9yoa3Iul_Eu5TA)
- [敏捷测试二三事](https://mp.weixin.qq.com/s/bKkGWJA3JhvdCjgg6-AVEQ)
- [软件测试中的虚拟化](https://mp.weixin.qq.com/s/zHyJiNFgHIo2ZaPFXsxQMg)
- [新词:QA-Ops](https://mp.weixin.qq.com/s/detcY6OVYmzOTUxfwN6CFQ)
- [生产环境中进行自动化测试](https://mp.weixin.qq.com/s/JKEGRLOlgpINUxs-6mohzA)
- [所谓UI测试](https://mp.weixin.qq.com/s/wDvUy_BhQZCSCqrlC2j1qA)
- [预上线环境失败的原因](https://mp.weixin.qq.com/s/jva0Jb2OMarERmTn7Kh2Ng)
- [自动化策略六步走](https://mp.weixin.qq.com/s/He69k8iCKhTKD1j-yV6M5g)
- [合格的测试经理必备技能](https://mp.weixin.qq.com/s/gFIYksHMn_bHEwAhmgVzjg)
- [质量保障的拓展实践](https://mp.weixin.qq.com/s/a3sd0dQnjk3TerOhfo-1ng)
- [敏捷领导者常见误区](https://mp.weixin.qq.com/s/xdq3CZflRjvDBGDLK4tNFQ)
- [功能自动化测试策略](https://mp.weixin.qq.com/s/qHmcblN4cD4JK6jT7oU4fQ)
- [性能测试、压力测试和负载测试](https://mp.weixin.qq.com/s/g26lpd7d7EtpN7pkiqkkjg)
- [如何维护自动化测试](https://mp.weixin.qq.com/s/4eh4AN_MiatMSkoCMtY3UA)
- [负载测试最佳实践](https://mp.weixin.qq.com/s/hNj7UsCCvv9TdexAcNFUvg)
- [有关UI测试计划](https://mp.weixin.qq.com/s/D0fMXwJF754a7Mr5ARY5tQ)
- [软件测试外包](https://mp.weixin.qq.com/s/sYQfb2PiQptcT0o_lLpBqQ)
- [避免PPT自动化的最佳实践](https://mp.weixin.qq.com/s/5YgYK4_YLZ1wDDhbwMTGlw)
- [如何优化软件测试成本](https://mp.weixin.qq.com/s/_eXrzDyNDA6yCRR8nPmzGA)
- [如何从手动测试转到自动化测试](https://mp.weixin.qq.com/s/EBDTX4AMnn2KTEjL88bOhQ)
- [Selenium自动化测试技巧](https://mp.weixin.qq.com/s/EzrpFaBSVITO2Y2UvYvw0w)
- [测试为何会错过Bug](https://mp.weixin.qq.com/s/UFHy8OwZjnMkB70roIS-zQ)
- [测试用例设计——一切测试的基础](https://mp.weixin.qq.com/s/0_ubnlhp2jk-jxHxJ95E9g)
- [移动应用测试:挑战,类型和最佳实践](https://mp.weixin.qq.com/s/kYxh6xki69evVDsXDxrYKQ)
- [敏捷测试中面临的挑战](https://mp.weixin.qq.com/s/vmsW56r1J7jWXHSZdcwbPg)
- [AI如何影响测试行业](https://mp.weixin.qq.com/s/d6c7u1-lAmsiIQz3UvcGKg)
- [自动测试失败的5个原因](https://mp.weixin.qq.com/s/bTakAHIcx_WyJIo-tsbvvg)
- [大促前必做的质量检查](https://mp.weixin.qq.com/s/iOku2wKnlr8pSZO0l9Q3Bw)
- [测试开发工程师工作技巧](https://mp.weixin.qq.com/s/TvrUCisja5Zbq-NIwy_2fQ)
- [敏捷回归测试](https://mp.weixin.qq.com/s/_bBQFggkZTTEqcb9R_68OA)
- [制定质量管理计划指南](https://mp.weixin.qq.com/s/ztXYE8EtwlkUdxnk1cjKVg)
- [质量管理计划的基本要素](https://mp.weixin.qq.com/s/v8lOioYn01S1F0ex4mmljA)
- [质量保障的方法和实践](https://mp.weixin.qq.com/s/hU_YCaZB-0a09dOCAVgcpw)
- [为什么测试覆盖率如此重要](https://mp.weixin.qq.com/s/0evyuiU2kdXDgMDnDKjORg)
- [自动化测试框架](https://mp.weixin.qq.com/s/vu6p_rQd3vFKDYu8JDJ0Rg)
- [敏捷中的端到端测试](https://mp.weixin.qq.com/s/cdi4xnEzDLpl9ncQguLuAQ)
- [自动化测试灵魂三问:是什么、为什么和做什么](https://mp.weixin.qq.com/s/geOejJx79-jTwafG9aXwqA)
- [基于代码的自动化和无代码自动化](https://mp.weixin.qq.com/s/8Dopihqs4XzpU-sN-I94kw)
- [物联网测试](https://mp.weixin.qq.com/s/B_JI4DANxoOq4HurxZC65Q)
- [功能测试知多少](https://mp.weixin.qq.com/s/vTxZLwlvlfIBv892Ji-oLQ)
- [如何选择自动化测试工具](https://mp.weixin.qq.com/s/yJo-d9bAZDs1Lcp8j7ISRg)
- [连续测试策略](https://mp.weixin.qq.com/s/0aD_0cUW83oPu3sl7sHNnQ)
- [如何设置质量检查流程](https://mp.weixin.qq.com/s/PQeXxMZzzU15xSfY5wkVgA)
- [编写干净的代码之变量篇](https://mp.weixin.qq.com/s/J9rGIe8a2xaLlNJq2nVmzw)
- [高效Mobile DevOps步骤](https://mp.weixin.qq.com/s/-qc-d_zJ1C9H_Uvd8gJiBw)
- [回归BUG](https://mp.weixin.qq.com/s/00j-acjPeKQ7uap62WpY3w)
- [处理回归BUG最佳实践](https://mp.weixin.qq.com/s/R3O2NruPAA2gQf4-3R6aAQ)
- [自动化测试实践清单](https://mp.weixin.qq.com/s/972WruGsYmkRroquBFoqMg)
- [自动化测试类型](https://mp.weixin.qq.com/s/GRkN8ozZiWNu21Y3KbVOBA)
- [无脚本测试](https://mp.weixin.qq.com/s/PVBxk4KEwCmWkB6mOXJFlw)
- [自动化测试转型挑战及其解决方案](https://mp.weixin.qq.com/s/BixS6jRdF5N_nvmW3_OthQ)
- [无数据驱动自动化测试](https://mp.weixin.qq.com/s/aCYRGxkzMogLbmACYo6ssw)
- [为什么自动化测试在敏捷开发中很重要](https://mp.weixin.qq.com/s/AP0wUQZ09NvSqme8e09igQ)
- [测试模型中理解压力测试和负载测试](https://mp.weixin.qq.com/s/smNLx3malzM3avkrn3EJiA)
- [移动测试工程师职业](https://mp.weixin.qq.com/s/dhtR4TbQNu5fWpmJkXGivw)
- [远程测试工作挑战](https://mp.weixin.qq.com/s/LK-GEN4OtuWVGDuG8psmOQ)
- [自动化测试用例的原子性](https://mp.weixin.qq.com/s/jA5WMHwJcu88nHXWoMBAdQ)
- [可测性经验分享](https://mp.weixin.qq.com/s/iRtUjESYS3sh3YTD-BWjdA)
- [敏捷中的回归测试的优化【译】](https://mp.weixin.qq.com/s/nDiZZgA1PIiAUCG_xwA2rA)
- [敏捷的主要优势【译】](https://mp.weixin.qq.com/s/zkI85TLI37XrPFaQ-pZYMA)
- [2021年自动化测试流行趋势【译】](https://mp.weixin.qq.com/s/dIZxkNT6mjgRukLBy0AJ6Q)
- [敏捷团队的自动化测试【译】](https://mp.weixin.qq.com/s/5BvzQvdssTyp8voC9J9www)
## 大咖风采
- [Tcloud 云测平台--集大成者](https://mp.weixin.qq.com/s/29sEO39_NyDiJr-kY5ufdw)
- [Android App 测试工具及知识大集合](https://mp.weixin.qq.com/s/Xk9rCW8whXOTAQuCfhZqTg)
- [Android App常规测试内容](https://mp.weixin.qq.com/s/tweeoS5wTqK3k7R2TVuDXA)
- [JVM的对象和堆](https://mp.weixin.qq.com/s/iNDpTz3gBK3By_bvUnrWOA)
# UI自动化
## UI自动化
- [自动化测试中java多线程的使用实例](https://mp.weixin.qq.com/s/BNSLaIdcTPTNj1tKpGf6fw)
- [自动化测试中递归函数的应用](https://mp.weixin.qq.com/s/86602zV9zYblhCRMiUlwdA)
- [Appium 2.0速览](https://mp.weixin.qq.com/s/mHHSZKYZXQby8YiQBP57hA)
## UiAutomator
- [android uiautomator一个画心形图案的方法--代码的浪漫](https://mp.weixin.qq.com/s/byfAKHxD2i83VHnuaNgIZA)
- [android UiAutomator了解源码解决控件bonds\[0,0\]无法点击](https://mp.weixin.qq.com/s/nu2ftXNUSG2_kmZjyhEcVA)
- [android UiAutomator在清除文本时遇到中文的解决办法](https://mp.weixin.qq.com/s/cNGNCoXsYBSk-MWTWLxF4g)
- [android UiAutomator获取当前页面某类控件个数的方法](https://mp.weixin.qq.com/s/njb19Sq_Kg4SusAS_eEuug)
- [android uiautomator自定义监听示例--一个弹出权限设置的监听](https://mp.weixin.qq.com/s/OKKZOf51yq6qY5D6PvN-gg)
- [如何在Mac OS上使用UiAutomator快速调试类](https://mp.weixin.qq.com/s/jm9d_42jp_BSlv-IW0BpzQ)
- [UiAutomator测试中如何恢复手机输入法](https://mp.weixin.qq.com/s/o4-zCgbdq6OsHRK9XT14QA)
- [android UiAutomator基本api的二次封装](https://mp.weixin.qq.com/s/_3jGg3ZYoeyAkjZpy8gWfQ)
- [android UiAutomator让运行失败的用例重新运行](https://mp.weixin.qq.com/s/tMOPbt1w9tRaKEuIZYKCyg)
- [利用UiAutomator写一个首页刷新的稳定性测试脚本](https://mp.weixin.qq.com/s/au9hAScsqUdcrh_usu8Pfg)
- [android UiAutomator长按实现控制按住控件时间的方法](https://mp.weixin.qq.com/s/lOvxAOMh6mmIh3CEV6Fduw)
- [android UiAutomator自定义快速调试类](https://mp.weixin.qq.com/s/iP2dTOeVkFMzU3dQ06R9GA)
- [利用UiAutomator写一个自动遍历渠道包关键功能的脚本](https://mp.weixin.qq.com/s/0vg2OlfTy0y4T6sWUG-olA)
- [android UiAutomator如何根据颜色判断控件的状态](https://mp.weixin.qq.com/s/kldsD3OZ4mJZ5yYQXfXxLw)
- [android UiAutomator控制多台手机同时运行用例的方法](https://mp.weixin.qq.com/s/z9vgpOQP0wQffmG4C_oBWg)
- [android UiAutomator使用递归函数写一个让屏幕一闪一闪提醒的方法](https://mp.weixin.qq.com/s/AzXjePdmsgs6QsICZOdPyw)
- [android UiAutomator获取视频播放进度的方法](https://mp.weixin.qq.com/s/ho070zX9rrLPmh8bZe_HgQ)
## Selenium
- [selenium2java截图保存桌面](https://mp.weixin.qq.com/s/OUfwsIo635coGONRNccYlg)
- [selenium2java调用JavaScript方法封装](https://mp.weixin.qq.com/s/t-Xs2Hr9TM2bjDiOqQX2mA)
- [selenium2java利用mysq解决向浏览器插入cookies时token过期问题](https://mp.weixin.qq.com/s/oAAkDKUGytQjxJLFkod-AQ)
- [selenium2java 遇到有三个窗口用例的处理办法](https://mp.weixin.qq.com/s/6AJBanVKYwlsNcvsu_25QQ)
- [selenium2java通过第三方登录绕过知乎登陆验证码](https://mp.weixin.qq.com/s/A5uTtxlg4l4pru2z7v1cug)
- [selenium2java使用select处理下拉框示例](https://mp.weixin.qq.com/s/FFor451WzuUzINeclGN-Ng)
- [selenium2java爬虫示例](https://mp.weixin.qq.com/s/vSZzpzEqsCtASSx6iHqxVA)
- [selenium2java写一个设置秒杀价的脚本](https://mp.weixin.qq.com/s/1ocIOYt3gdGIJrd9v2shhg)
- [selenium2java基本方法二次封装](https://mp.weixin.qq.com/s/2GaXigt13wa6JgxJkcef5g)
- [selenium2java一个弹框上传时间日期大杂烩测试用例](https://mp.weixin.qq.com/s/Z8ZeZ-zFy0q0a-e_epT1Kg)
- [selenium2java造数据例子](https://mp.weixin.qq.com/s/ACO2O5f7Po4Qn242lopMBg)
- [selenium2java让浏览器停止加载的方法](https://mp.weixin.qq.com/s/aBQdGYys3Bpyf6yigGOCIA)
- [selenium2java写一个强制刷新页面的方法](https://mp.weixin.qq.com/s/VWW7cH5WSDmw_eCabUh9LQ)
- [selenium2java通过接口获取并注入cookies](https://mp.weixin.qq.com/s/luLHWxPWSekuDMbnKsfJvg)
- [Selenium编写自动化用例的8种技巧](https://mp.weixin.qq.com/s/8wRHc_krXNfWclNeOJDNPg)
- [JUnit中用于Selenium测试的中实践](https://mp.weixin.qq.com/s/KG4sltQMCfH2MGXkRdtnwA)
- [您如何使用Selenium来计算自动化测试的投资回报率?](https://mp.weixin.qq.com/s/DVSEm0DhoAvYfTWIniabJg)
- [Selenium 4 Java的最佳测试框架](https://mp.weixin.qq.com/s/MlNyv-kb03gRTcYllxUreA)
- [Selenium 4.0 Alpha更新日志](https://mp.weixin.qq.com/s/tU7sm-pcbpRNwDU9D3OVTQ)
- [Selenium 4.0 Alpha更新实践](https://mp.weixin.qq.com/s/yT9wpO5o5aWBUus494TIHw)
- [JUnit 5和Selenium基础(一)](https://mp.weixin.qq.com/s/ehBRf7st-OxeuvI_0yW3OQ)
- [JUnit 5和Selenium基础(二)](https://mp.weixin.qq.com/s/Gt82cPmS2iX-DhKXTXiy8g)
- [JUnit 5和Selenium基础(三)](https://mp.weixin.qq.com/s/8YkonXTYgAV5-pLs9yEAVw)
- [如何在跨浏览器测试中提高效率](https://mp.weixin.qq.com/s/MB_Wv7yQ6i9BztAZtL4grA)
- [Selenium Python使用技巧(一)](https://mp.weixin.qq.com/s/39v8tXG3xig63d-ioEAi8Q)
- [Selenium Python使用技巧(二)](https://mp.weixin.qq.com/s/uDM3y9zoVjaRmJJJTNs6Vw)
- [Selenium Python使用技巧(三)](https://mp.weixin.qq.com/s/J7-CO-UDspUGSpB8isjsmQ)
- [Selenium并行测试基础](https://mp.weixin.qq.com/s/OfXipd7YtqL2AdGAQ5cIMw)
- [Selenium并行测试最佳实践](https://mp.weixin.qq.com/s/-RsQZaT5pH8DHPvm0L8Hjw)
- [维护Selenium测试自动化的最佳实践](https://mp.weixin.qq.com/s/EMD1aWuzOSfT7j3KeXhJcA)
- [Selenium自动化测试技巧](https://mp.weixin.qq.com/s/EzrpFaBSVITO2Y2UvYvw0w)
- [Selenium自动化:代码测试与无代码测试](https://mp.weixin.qq.com/s/gtmLpQ5FCeuzh1SB5mxuvg)
- [Selenium处理下拉列表](https://mp.weixin.qq.com/s/E2txSVAmDzYIEZWnyAND4g)
- [Selenium自动化常见问题](https://mp.weixin.qq.com/s/edoxu-QaD0SOw1VqrhCZWA)
- [Selenium4 IDE,它终于来了](https://mp.weixin.qq.com/s/XNotlZvFpmBmBQy1pYifOw)
- [Selenium4 IDE特性:无代码趋势和SIDE Runner](https://mp.weixin.qq.com/s/G0S9K0jHsN0P_jxdMME-cg)
- [Selenium4 IDE特性:弹性测试、循环和逻辑判断](https://mp.weixin.qq.com/s/o4_jIyi9O7s4S3CbTzl5rQ)
- [Selenium自动化最佳实践技巧(上)](https://mp.weixin.qq.com/s/lZww1azmncMMMHRY0_yKqA)
- [Selenium自动化最佳实践技巧(中)](https://mp.weixin.qq.com/s/9D0lUZ-XKHiukNeRqp6zOQ)
- [Selenium自动化最佳实践技巧(下)](https://mp.weixin.qq.com/s/opVik2ZxmTBurIBoa4yipQ)
- [Selenium等待:sleep、隐式、显式和Fluent](https://mp.weixin.qq.com/s/73BobMq9M12rYMvzxNhRtA)
- [Selenium自动化的JUnit参数化实践](https://mp.weixin.qq.com/s/WFu5rJaowxhAIcbEoEatkw)
- [Selenium异常集锦](https://mp.weixin.qq.com/s/DDkaliSVthX-c_KKG-WwNA)
- [Selenium自动化测试之前](https://mp.weixin.qq.com/s/DKjSnS9sP0SoHUw4OhOikw)
## APP性能
- [使用monkey测试时,一个控制WiFi状态的多线程类](https://mp.weixin.qq.com/s/P8HVtzHBlj_FcDAAHFKBDg)
- [java执行和停止Logcat命令及多线程实现](https://mp.weixin.qq.com/s/sUYibRc-muxQoxi48QiaRg)
- [APP性能测试中获取CPU和PSS数据多线程实现](https://mp.weixin.qq.com/s/NiJSZ8VxpdnarbDJjcJziA)
- [统计APP启动时间和进入首页时间的多线程类](https://mp.weixin.qq.com/s/IMs6vd3H-HF65Vb-zPwDhw)
- [如何获取手机性能测试数据FPS](https://mp.weixin.qq.com/s/qZy5AQkNpUXRJk46BHVzaQ)
- [一个循环启动APP并保持WiFi常开的多线程类](https://mp.weixin.qq.com/s/OgdT4IffDyAdkKmO2SS9iQ)
## 杂乱
- [测试窝,首页抄我七篇原创还拉黑,你们的良心不会痛吗?](https://mp.weixin.qq.com/s/ke5avkknkDMCLMAOGT7wiQ)
- [如何优雅地屏蔽掉Google搜索结果中视频、新闻、图片等结果](https://mp.weixin.qq.com/s/Iu7pt4Qk3w9sJp3n_UVAeQ)
- [测试玩梗--欢迎补充](https://mp.weixin.qq.com/s/y_QHbsjFCQVSCfj-A4Usmg)
- [图解HTTP脑图](https://mp.weixin.qq.com/s/100Vm8FVEuXs0x6rDGTipw)
- [测试之JVM命令脑图](https://mp.weixin.qq.com/s/qprqyv0j3SCvGw1HMjbaMQ)
- [好书推荐《Java性能权威指南》](https://mp.weixin.qq.com/s/YWd5Yx6n7887g1lMLTcsWQ)
- [2019年浏览器市场份额排行榜](https://mp.weixin.qq.com/s/4NmJ_ZCPD5UwaRCtaCfjEg)
- [JSON基础](https://mp.weixin.qq.com/s/tnQmAFfFbRloYp8J9TYurw)
- [JMeter吞吐量误差分析](https://mp.weixin.qq.com/s/jHKmFNrLmjpihnoigNNCSg)
- [JMeter如何模拟不同的网络速度](https://mp.weixin.qq.com/s/1FCwNN2htfTGF6ItdkcCzw)
- [疫情期间,如何提高远程办公效率](https://mp.weixin.qq.com/s/k_XrdqjGKMshK2Ea-VCNLw)
- [接口测试视频专题](https://mp.weixin.qq.com/s/4mKpW3QiVRee3kcVOSraog)
- [Groovy在JMeter中应用专题](https://mp.weixin.qq.com/s/KcxPUDWl7MLQemFRoIV92A)
- [Java多线程编程在JMeter中应用](https://mp.weixin.qq.com/s/xCnFx5TvIF1SAVNm-aZnxQ)
- [未来的神器fiddler Everywhere](https://mp.weixin.qq.com/s/-BSuHR6RPkdv8R-iy47MLQ)
- [Charles报错Failed to install helper解决方案](https://mp.weixin.qq.com/s/LHhMTBhlDM0DrPCvWeU0zA)
- [测试仓库推介(上)](https://mp.weixin.qq.com/s/zgy6UgNMFcbISD1NhxSAWg)
- [测试仓库推介(下)](https://mp.weixin.qq.com/s/njnpmRGoEgdxjqkR7c3a6A)
- [Fiddler Everywhere工具答疑](https://mp.weixin.qq.com/s/2peWMJ-rgDlVjs3STNeS1Q)
- [Mac上测试Internet Explorer的N种方法](https://mp.weixin.qq.com/s/HeLBPTp2dfs5IlyLMCi90Q)
- [IntelliJ中基于文本的HTTP客户端](https://mp.weixin.qq.com/s/-9qi_lLVVfxQKEFmcRYFtA)
- [开源礼节](https://mp.weixin.qq.com/s/EyNules2f9NYdnYAX_NQSw)
- [弱网测试:最低流畅网速是多少?](https://mp.weixin.qq.com/s/rCji6fZs9yYyk7GyIWvSiA)
- [接口测试直播回顾](https://mp.weixin.qq.com/s/B8ih9sswaE-OWuVib6C16g)
- [SpotBugs报错no Groovy library is defined解决办法](https://mp.weixin.qq.com/s/XxvuVS2TmlqT5-b22vObYQ)
- [推荐好书:不要总是谦卑地弯着腰](https://mp.weixin.qq.com/s/mYNN9jSaikOF5aJEkb-Bug)
- [2020年FunTester自我总结](https://mp.weixin.qq.com/s/DeDY1JZUTk3cjjQfr3DJRg)
- [原创打油诗欣赏](https://mp.weixin.qq.com/s/3hPSDjH-3cWu6EVsjU0wOw)
- [优秀讲师 | 腾讯云+社区权威认证](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)
- [Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g)
- [GDevOps官方合作媒体](https://mp.weixin.qq.com/s/OnwvRqrgXq_pGp8eOXOTgw)
- [假期思考题](https://mp.weixin.qq.com/s/3DOnkmYDlwk-XKg4ge3ZUw)
- [甩锅技能+1](https://mp.weixin.qq.com/s/nMwlfXZoDcRRPHcTKpvfNg)
- [不要浪费自己的求知欲](https://mp.weixin.qq.com/s/WO0aQqmhU_xGUpWvYwOqUA)
================================================
FILE: document/base.markdown
================================================
# 基础篇
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),非著名测试开发er,欢迎关注。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 语言合集
## Java
- [java一行代码打印心形](https://mp.weixin.qq.com/s/QPSryoSbViVURpSa9QXtpg)
- [操作的原子性与线程安全](https://mp.weixin.qq.com/s/QU3llkGLepX2VCch8Y9GKw)
- [快看,i++真的不安全](https://mp.weixin.qq.com/s/-CdWdROKSEq_ZiLX2kWxzA)
- [原子操作组合与线程安全](https://mp.weixin.qq.com/s/XB5LXucAF5Bo8EkfLZYRmw)
- [java利用for循环输出正三角新解](https://mp.weixin.qq.com/s/nnMR2177LLVn4u_9s9Fl4g)
- [在main方法之前,到底执行了什么?](https://mp.weixin.qq.com/s/jWxiCMfwmvRHrjPdRG8ZyQ)
- [传参传的到底是什么?](https://mp.weixin.qq.com/s/p_pEQwE6h6q7PprkW-kjbg)
- [json里面put了null会怎么样?](https://mp.weixin.qq.com/s/gQVROe01I3JzIqNdTSHpDQ)
- [主线程都结束了,为何进程还在执行](https://mp.weixin.qq.com/s/q2v5JU5dtmNEol7I7IVY-Q)
- [java测试框架如何执行groovy脚本文件](https://mp.weixin.qq.com/s/0GYt1l3_z5-1qzBNl6_PzA)
- [java用递归筛选法求N以内的孪生质数(孪生素数)](https://mp.weixin.qq.com/s/PSdCb-DrgMPb4WpJJMexmQ)
- [从JVM堆内存分析验证深浅拷贝](https://mp.weixin.qq.com/s/SdYDnoau1rjjvPC2SUymBg)
- [如何学习Java基础](https://mp.weixin.qq.com/s/FCPStkYoJF67NYln4Lc6xg)
- [如何保存HTTPrequestbase和CloseableHttpResponse](https://mp.weixin.qq.com/s/gRY8HRQHCh0PfyS7Q22IwA)
- [如何在匿名thread子类中保证线程安全](https://mp.weixin.qq.com/s/GCXx_-ummi0JfZQ7GTIxig)
- [Java服务端两个常见的并发错误](https://mp.weixin.qq.com/s/5VvCox3eY6sQDsuaKB4ZIw)
- [Java中interface属性和实例方法](https://mp.weixin.qq.com/s/vrKkM6522tgw3v_cL7R8HA)
- [服务端性能优化之双重检查锁](https://mp.weixin.qq.com/s/-bOyHBcqFlJY3c0PEZaWgQ)
- [Java并发BUG基础篇](https://mp.weixin.qq.com/s/NR4vYx81HtgAEqH2Q93k2Q)
- [Java并发BUG提升篇](https://mp.weixin.qq.com/s/GCRRe8hJpe1QJtxq9VBEhg)
- [性能测试中图形化输出测试数据](https://mp.weixin.qq.com/s/EMvpYIsszdwBJFPIxztTvA)
- [超大对象导致Full GC超高的BUG分享](https://mp.weixin.qq.com/s/L15-0JW9WK-E005GeOG9WQ)
- [利用ThreadLocal解决线程同步问题](https://mp.weixin.qq.com/s/VEm8jt3ZUEUdyyeXPC8VvQ)
- [线程安全集合类中的对象是安全的么?](https://mp.weixin.qq.com/s/WKSuPEfzZCVwjVTcoD0Dyg)
- [如何使用“dd MM”解析日期](https://mp.weixin.qq.com/s/v9ooAj3dKu53JXgxB482HA)
- [Java和Groovy正则使用](https://mp.weixin.qq.com/s/DT3BKE3ZcCKf6TLzGc5wbg)
- [运行越来越快的Java热点代码](https://mp.weixin.qq.com/s/AP0BcDEjDuaouaB0RXJOoQ)
- [6个重要的JVM性能参数](https://mp.weixin.qq.com/s/b1QnapiAVn0HD5DQU9JrIw)
- [ArrayList浅、深拷贝](https://mp.weixin.qq.com/s/kYsBzFsCyDPUssdV3MDqLA)
- [Java性能测试中两种锁的实现](https://mp.weixin.qq.com/s/j9dGFvYzCJ0AGwYUtTrTsw)
- [测试如何处理Java异常](https://mp.weixin.qq.com/s/H00GWiATOD8QHJu3UewrBw)
- [创建Java守护线程](https://mp.weixin.qq.com/s/_UjWdvq8QWYTshr4SeniBg)
- [Lambda表达式在线程安全Map中应用](https://mp.weixin.qq.com/s/zZjB5aOWh4a_k1eoEsR5ww)
- [Java程序是如何浪费内存的](https://mp.weixin.qq.com/s/w7VF5m5cc0X7LNvqmwGfvg)
- [Java中的自定义异常](https://mp.weixin.qq.com/s/nspIdxFP9qEDtagGN4gaMQ)
- [Java文本块](https://mp.weixin.qq.com/s/GwasvpJsd7uLngvCr6KlQw)
- [CountDownLatch类在性能测试中应用](https://mp.weixin.qq.com/s/uYBPPOjauR2h81l2uKMANQ)
- [CyclicBarrier类在性能测试中应用](https://mp.weixin.qq.com/s/kvEHX3t_2xpMke9vwOdWrg)
- [Phaser类在性能测试中应用](https://mp.weixin.qq.com/s/plxNnQq7yNQvHYEGpyY4uA)
- [Java压缩/解压缩字符串](https://mp.weixin.qq.com/s/7vHNd5dEN93DPUqgS8od_A)
- [Java删除空字符:Java8 & Java11](https://mp.weixin.qq.com/s/6dlgYgTFZsHuJ4Eaby5eyg)
- [Java Stream中map和flatMap方法](https://mp.weixin.qq.com/s/0FG2o7VUAG6z8a_0je-1EQ)
- [泛型类的正确用法](https://mp.weixin.qq.com/s/1azilraonPIZNCnw_9MB5Q)
- [Java字符串到数组的转换--最后放大招](https://mp.weixin.qq.com/s/iMUYZYkJ5CjykwWqinNm5g)
- [Java求数组的并集--最后放大招](https://mp.weixin.qq.com/s/bZ93SGakyiRbaRujhx4nvw)
- [Java计算数组平均值--最后放大招](https://mp.weixin.qq.com/s/dxQaFHu2PyAbOK6jpEgEUQ)
- [Math.abs()求绝对值返回负值BUG分享](https://mp.weixin.qq.com/s/RHzExuRqF1XsBtzGKzmgGA)
- [Java代理模式初探](https://mp.weixin.qq.com/s/SBL_K2PQez3vDHhtAN9NLg)
- [Socket接口异步验证实践](https://mp.weixin.qq.com/s/bnjHK3ZmEzHm3y-xaSVkTw)
- [性能测试中异步展示测试进度](https://mp.weixin.qq.com/s/AOERJbEc4ATJqhjvnxgQoA)
- [Java中的ThreadLocal功能演示](https://mp.weixin.qq.com/s/n92k1JswHKrqT7Y_CD9Q0w)
- [ThreadLocal在链路性能测试中实践](https://mp.weixin.qq.com/s/3qhNdHHSStELzNraQSpcew)
- [歪解字符串中连续出现次数最多问题](https://mp.weixin.qq.com/s/xBy4iB4qLd4WQgCsVVuemw)
- [Java&Groovy下载文件对比](https://mp.weixin.qq.com/s/T9WUynej2yOZhCkDUhaLYw)
- [线程同步类CyclicBarrier在性能测试集合点应用](https://mp.weixin.qq.com/s/K2YySxX9T4v_rzbvIbIHJA)
- [Java线程同步三剑客](https://mp.weixin.qq.com/s/cAmd11-HdwXNU3tp4TiDLg)
## Groovy
- [java和groovy混合编程时提示找不到符合错误解决办法](https://mp.weixin.qq.com/s/dLC2W7nIi5zCuK6JTkiA-w)
- [groovy使用stream语法递归筛选法求N以内的质数](https://mp.weixin.qq.com/s/TsrVn1cuQUrU6wj9OnR-FQ)
- [使用Groovy进行Bash(shell)操作](https://mp.weixin.qq.com/s/fgCTlZUF3QeNj6jzq1ZgGg)
- [使用Groovy和Gradle轻松进行数据库操作](https://mp.weixin.qq.com/s/lwmclrnW0csykVRhu7dNTQ)
- [愉快地使用Groovy Shell](https://mp.weixin.qq.com/s/fJh7fbB3naBFBEiaS62oxw)
- [Gradle+Groovy基础篇](https://mp.weixin.qq.com/s/c2j7G-PoNtAB3oYYDUhCGw)
- [Gradle+Groovy提高篇](https://mp.weixin.qq.com/s/yXmYj_1fynLkR0-5FV_Arw)
- [Groovy重载操作符](https://mp.weixin.qq.com/s/4jW06Q4_vjFR9DovRTTuHg)
- [用Groovy处理JMeter断言和日志](https://mp.weixin.qq.com/s/Q4yPA4p8dZYAARZ60ZDh9w)
- [用Groovy处理JMeter变量](https://mp.weixin.qq.com/s/BxtweLrBUptM8r3LxmeM_Q)
- [用Groovy在JMeter中执行命令行](https://mp.weixin.qq.com/s/VTip7tiLpwBOr1gUoZ0n8A)
- [用Groovy处理JMeter中的请求参数](https://mp.weixin.qq.com/s/9pCUOXWpMwXR5ynvCMYJ7A)
- [Java和Groovy正则使用](https://mp.weixin.qq.com/s/DT3BKE3ZcCKf6TLzGc5wbg)
- [Groovy中的元组](https://mp.weixin.qq.com/s/0-ka0-tv1vyKbiA6m44jRw)
- [从Java到Groovy的八级进化论](https://mp.weixin.qq.com/s/QTrRHsD3w-zLGbn79y8yUg)
- [用Groovy在JMeter中使用正则提取赋值](https://mp.weixin.qq.com/s/9riPpnQZCfKGscuzOOpYmQ)
- [Groovy在JMeter中处理cookie](https://mp.weixin.qq.com/s/DCnDjWaj2aiKv5HVw3-n6A)
- [Groovy在JMeter中处理header](https://mp.weixin.qq.com/s/juY-1jEWODJ5HHiEsxhIEw)
- [Groovy的神奇NullObject](https://mp.weixin.qq.com/s/jLGisN_30PrCgNP33Sww0g)
- [Groovy中的list](https://mp.weixin.qq.com/s/0mUe1_WrUiEm1t6kqCV3eQ)
- [JMeter参数签名——Groovy脚本形式](https://mp.weixin.qq.com/s/wQN9-xAUQofSqiAVFXdqug)
- [Groovy中的闭包](https://mp.weixin.qq.com/s/pfcG47gSPfUveAaEfdeo8A)
- [JMeter参数签名——Groovy工具类形式](https://mp.weixin.qq.com/s/urwU4p9ofv9sU-JFy5Z0iA)
- [删除List中null的N种方法--最后放大招](https://mp.weixin.qq.com/s/4mfskN781dybyL59dbSbeQ)
- [混合Java函数和Groovy闭包](https://mp.weixin.qq.com/s/FAIzGgLSX2u7RKbOGs3lGA)
- [Groovy重载操作符(终极版)](https://mp.weixin.qq.com/s/4oYGJ2B2Y1AqxsIj8v5nZA)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [Groovy小记it关键字和IDE报错](https://mp.weixin.qq.com/s/cIMHzkvKtH0a0ewkiBnV8g)
- [JsonPath验证类既Groovy重载操作符实践](https://mp.weixin.qq.com/s/5gc04CAsBY6pWxe5c2P41w)
- [Groovy枚举类初始化异常分析](https://mp.weixin.qq.com/s/koFhpBZM1MFYYxCNxUKPyQ)
- [Java&Groovy下载文件对比](https://mp.weixin.qq.com/s/T9WUynej2yOZhCkDUhaLYw)
## Python
- [python使用filter方法递归筛选法求N以内的质数(素数)--附一行打印心形标记的代码解析](https://mp.weixin.qq.com/s/D8RfpdIi8smCL8TAzBcNpA)
- [关于python版微信使用经验分享](https://mp.weixin.qq.com/s/19IaI6ETZAm_T4ePPlXqIg)
- [python用递归筛选法求N以内的孪生质数(孪生素数)](https://mp.weixin.qq.com/s/rVY2pTl8So11WCvA9GrFbA)
- [利用python wxpy和requests写一个自动应答微信机器人实例](https://mp.weixin.qq.com/s/Fni2kX5BRjdqOQ-glCLjRg)
- [Python版Socket.IO接口测试脚本](https://mp.weixin.qq.com/s/oXBP6Sx3yPqlmvV9uCUScw)
## 测开笔记
- [我的开发日记(一)](https://mp.weixin.qq.com/s/eQgpOKbXsU9vOmxp0Xiklg)
- [我的开发日记(二)](https://mp.weixin.qq.com/s/XuffL3ZmKKOgHDtH_cEYOw)
- [我的开发日记(三)](https://mp.weixin.qq.com/s/a-I0agh6nWp8RLlcmbgf5w)
- [我的开发日记(四)](https://mp.weixin.qq.com/s/QukXd00Mx_dbkgiXys0FNg)
- [我的开发日记(五)](https://mp.weixin.qq.com/s/6P3nScsVW6MfMcyIqcA1AQ)
- [我的开发日记(六)](https://mp.weixin.qq.com/s/Gz2QmukONNldSy9Fd29u5w)
- [我的开发日记(七)](https://mp.weixin.qq.com/s/MjZ-nFXfQkHMsXS0fX1c1w)
- [我的开发日记(八)](https://mp.weixin.qq.com/s/6ZhNcFm-gR5dhKQjEkE3Rg)
- [我的开发日记(九)](https://mp.weixin.qq.com/s/VfD2T3orojGxnylr3Q5UeA)
- [我的开发日记(十)](https://mp.weixin.qq.com/s/6DWth40LGbAraJi05G16Pw)
- [我的开发日记(十一)](https://mp.weixin.qq.com/s/nsX5A-P6QbePHDN_Pse0_A)
- [我的开发日记(十二)](https://mp.weixin.qq.com/s/XA1KJXBP3Zl-XFswXxUtvg)
- [我的开发日记(十三)](https://mp.weixin.qq.com/s/_QPUu5pUlg4A_AlC5wOGkA)
- [我的开发日记(十四)](https://mp.weixin.qq.com/s/Qy1YKAb3wqW_Ip2FwH7Otw)
- [我的开发日记(十五)](https://mp.weixin.qq.com/s/bwkvz2t6YItQD0O_BIxpHQ)
- [这些年,我写过的BUG(一)](https://mp.weixin.qq.com/s/mVTmT1FdwWl1e0BaL7Ne1g)
- [这些年,我写过的BUG(二)](https://mp.weixin.qq.com/s/NMz5n0ZMf6taGb-gr1BLyw)
- [FunTester测试框架架构图初探](https://mp.weixin.qq.com/s/bcMbVDkWbHSXjZFDeFyJsQ)
- [FunTester测试项目架构图初探](https://mp.weixin.qq.com/s/wqb8FXRbEXrhDuZounmNXA)
================================================
FILE: document/directory.markdown
================================================
* 由于文章链接存在敏感词问题,请各位看官移步
## [GitHub地址](https://github.com/JunManYuanLong/FunTester/blob/okay/document/article.markdown)
### 当然也可以关注**FunTester**公众号
================================================
FILE: document/update.markdown
================================================
# 升级篇
> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.com/s/CjwFBoS9sew6R74mM9QO4g),非著名测试开发er,欢迎关注。
* `Gitee`地址*https://gitee.com/fanapi/tester*
* `GitHub`地址*https://github.com/JunManYuanLong/FunTester*
# 案例分享
## 测试方案
- [如何对消息队列做性能测试](https://mp.weixin.qq.com/s/MNt22aW3Op9VQ5OoMzPwBw)
- [如何对修改密码接口进行压测](https://mp.weixin.qq.com/s/9CL_6-uZOlAh7oeo7NOpag)
- [如何测试概率型业务接口](https://mp.weixin.qq.com/s/kUVffhjae3eYivrGqo6ZMg)
- [如何测试非固定型概率算法P=p(1+0.1*N)](https://mp.weixin.qq.com/s/sgg8v-Bi-_sUDJXwuTCMGg)
- [性能测试中标记每个请求](https://mp.weixin.qq.com/s/PokvzoLdVf_y9inlVXHJHQ)
- [如何对N个接口按比例压测](https://mp.weixin.qq.com/s/GZxbH4GjDkk4BLqnUj1_kw)
- [多种登录方式定量性能测试方案](https://mp.weixin.qq.com/s/WuZ2h2rr0rNBgEvQVioacA)
- [压测中测量异步写入接口的延迟](https://mp.weixin.qq.com/s/odvK1iYgg4eRVtOOPbq15w)
- [绑定手机号性能测试](https://mp.weixin.qq.com/s/K5x1t1dKtIT2NKV6k4v5mw)
- [手机号验证码登录性能测试](https://mp.weixin.qq.com/s/i-j8fJAdcsJ7v8XPOnPDAw)
- [重放浏览器单个请求性能测试实践](https://mp.weixin.qq.com/s/a10hxCrIzS4TV9JwmDSI3Q)
- [重放浏览器多个请求性能测试实践](https://mp.weixin.qq.com/s/Hm1Kpp1PMrZ5rYFW8l2GlA)
- [重放浏览器请求多链路性能测试实践](https://mp.weixin.qq.com/s/9YSBLAyHVw8Z6IfK-nJTpQ)
- [Socket接口固定QPS性能测试实践](https://mp.weixin.qq.com/s/I9-14L8THxvtX1NJY0KPfw)
## BUG集锦
- [一个MySQL索引引发的血案](https://mp.weixin.qq.com/s/KLSber-gPg53JVfsCa3Dtw)
- [微软Zune闰年BUG分析](https://mp.weixin.qq.com/s/zpqAUcNcHaZjWUdUYH_loQ)
- [“双花”BUG的测试分享](https://mp.weixin.qq.com/s/0dsBsssNfg-seJ_tu9zFaQ)
- [iOS 11计算器1+2+3=24真的是bug么?](https://mp.weixin.qq.com/s/nokQhe_Hqcq-o7pZJmFlqQ)
- [不要在遍历的时候删除](https://mp.weixin.qq.com/s/MIczbEpbOrADL0_V7ZUhlg)
- [连开100年会员会怎样](https://mp.weixin.qq.com/s/mZw-SFIxFFbE-o8UeXhdfg)
- [异步查询转同步加redis业务实现的BUG分享](https://mp.weixin.qq.com/s/ni3f6QTxw0K-0I3epvEYOA)
- [Java服务端两个常见的并发错误](https://mp.weixin.qq.com/s/5VvCox3eY6sQDsuaKB4ZIw)
- [超大对象导致Full GC超高的BUG分享](https://mp.weixin.qq.com/s/L15-0JW9WK-E005GeOG9WQ)
- [访问权限导致toString返回空BUG分享](https://mp.weixin.qq.com/s/usDOcuJrXOmEKN-mVBzRKg)
- [异常使用中的BUG](https://mp.weixin.qq.com/s/IG9Ar3IT7CrlSv4d0lCvgA)
- [Math.abs()求绝对值返回负值BUG分享](https://mp.weixin.qq.com/s/RHzExuRqF1XsBtzGKzmgGA)
## 爬虫实践
- [接口爬虫之网页表单数据提取](https://mp.weixin.qq.com/s/imJ5u67xhYQaEzv-O1in4g)
- [httpclient爬虫爬取汉字拼音等信息](https://mp.weixin.qq.com/s/w-IvBxAsotmPA3pydpIo1w)
- [httpclient爬虫爬取电影信息和下载地址实例](https://mp.weixin.qq.com/s/TB49X4S-ioyoW5CrzAnHcw)
- [httpclient 多线程爬虫实例](https://mp.weixin.qq.com/s/nXL-MP4Y6CN2hgZQefWEeQ)
- [groovy爬虫练习之——企业信息](https://mp.weixin.qq.com/s/1TisDceIL1-Luqz_wOqAiw)
- [httpclient 爬虫实例——爬取三级中学名](https://mp.weixin.qq.com/s/Dd7U30aHYauqBFxJdxaiyg)
- [电子书网站爬虫实践](https://mp.weixin.qq.com/s/KGW0dIS5NTLgxyhSjxDiOw)
- [groovy爬虫实例——历史上的今天](https://mp.weixin.qq.com/s/5LDUvpU6t_GZ09uhZr224A)
- [爬取720万条城市历史天气数据](https://mp.weixin.qq.com/s/vOyKpeGlJSJp9bQ8NIMe2A)
- [记一次失败的爬虫](https://mp.weixin.qq.com/s/SMylrZLXDGw5f1xKI9ObnA)
- [爬虫实践--CBA历年比赛数据](https://mp.weixin.qq.com/s/mM_QSQddabU5im_O6iVR-Q)
- [图片爬虫实践](https://mp.weixin.qq.com/s/u5bRSyKsmn3TcjqEEqRJpw)
# 工具合集
## JsonPath合集
- [JsonPath实践(一)](https://mp.weixin.qq.com/s/Cq0_v_ptbGd4f5y8HIsq7w)
- [JsonPath实践(二)](https://mp.weixin.qq.com/s/w_iJTiuQahIw6U00CJVJZg)
- [JsonPath实践(三)](https://mp.weixin.qq.com/s/58A3k0T6dbOkBJ5nRYKDqA)
- [JsonPath实践(四)](https://mp.weixin.qq.com/s/8ER61qrkMj8bdBpyuq9r6w)
- [JsonPath实践(五)](https://mp.weixin.qq.com/s/knVLW960WXnckGLstdrOVQ)
- [JsonPath实践(六)](https://mp.weixin.qq.com/s/ckBCK3t1w68FLBhaw5a7Jw)
- [JsonPath工具类封装](https://mp.weixin.qq.com/s/KyuCuG5fVEExxBdGJO2LdA)
- [JsonPath工具类单元测试](https://mp.weixin.qq.com/s/1YtUWGk_sTjn9bHwAeT0Ew)
- [JsonPath验证类既Groovy重载操作符实践](https://mp.weixin.qq.com/s/5gc04CAsBY6pWxe5c2P41w)
- [JSON对象标记语法验证类](https://mp.weixin.qq.com/s/jSXmoEdMF7nWAqQuzJ5GiQ)
## Jacoco覆盖率
- [接口测试代码覆盖率(jacoco)方案分享](https://mp.weixin.qq.com/s/D73Sq6NLjeRKN8aCpGLOjQ)
- [jacoco无法读取build.xml配置中源码路径解决办法](https://mp.weixin.qq.com/s/8_x0rVfkIi-uX3y0drx_jw)
- [使用JaCoCo Maven插件创建代码覆盖率报告](https://mp.weixin.qq.com/s/4Jo05k2WxytiSSNW9WTV-A)
- [Java 8,Jenkins,Jacoco和Sonar进行持续集成](https://mp.weixin.qq.com/s/dOoXnKnWtQmmC5itClsl4g)
- [jacoco测试覆盖率过滤非业务类](https://mp.weixin.qq.com/s/7YGe9pCHw3wd87tgOlKjSA)
## arthas诊断工具
- [arthas快速入门视频演示](https://mp.weixin.qq.com/s/Wl5QMD52isGTRuAP4Cpo-A)
- [arthas进阶thread命令视频演示](https://mp.weixin.qq.com/s/XuF7Nr1sGC3diIn50zlDDQ)
- [arthas命令jvm,sysprop,sysenv,vmoption视频演示](https://mp.weixin.qq.com/s/87BsTYqnTCnVdG3a_kBcng)
- [arthas命令logger动态修改日志级别--视频演示](https://mp.weixin.qq.com/s/w724P9B12eTC9rMbavwsMA)
- [arthas命令sc和sm视频演示](https://mp.weixin.qq.com/s/Ga63sjW_bOKQqfnA5LTb9w)
- [arthas命令ognl视频演示](https://mp.weixin.qq.com/s/cMCaXFwjp6QHFq40TvP4bQ)
- [arthas命令redefine实现Java热更新](https://mp.weixin.qq.com/s/2HUXfJhoUfg4yMzSoRHK9w)
- [arthas命令monitor监控方法执行](https://mp.weixin.qq.com/s/7-oe3UoTY8bzpi89tIKvQQ)
- [arthas命令watch观察方法调用(上)](https://mp.weixin.qq.com/s/6fMKP7H4Q7ll_0v-wyN19g)
- [arthas命令watch观察方法调用(下)](https://mp.weixin.qq.com/s/-r2kufxdOjRb2TgF2HPskg)
- [arthas命令trace追踪方法链路](https://mp.weixin.qq.com/s/bzkdKZugkOl8C-_xTw92YA)
- [arthas命令tt方法时空隧道](https://mp.weixin.qq.com/s/mDczYmVdSmL5ZbK7bb8i0A)
## moco API
- [解决moco框架API在post请求json参数情况下query失效的问题](https://mp.weixin.qq.com/s/V5lXoepEBtPJrSUHA0Uz5A)
- [给moco API添加limit功能](https://mp.weixin.qq.com/s/pXJECi15ieNLmA0uIqEqfA)
- [给moco API添加random功能](https://mp.weixin.qq.com/s/YTcbFbFaWB5arW_fubgTTQ)
- [解决moco框架API在cycle方法缺失的问题](https://mp.weixin.qq.com/s/YfsPa7eW8WV65CDbPooBPg)
- [五行代码构建静态博客](https://mp.weixin.qq.com/s/hZnimJOg5OqxRSDyFvuiiQ)
- [moco API模拟框架视频讲解(上)](https://mp.weixin.qq.com/s/X5-fFXe018_O60WCRdawZg)
- [moco API模拟框架视频讲解(中)](https://mp.weixin.qq.com/s/g2En-9W9JWYrCLQr_WPEBA)
- [moco API模拟框架视频讲解(下)](https://mp.weixin.qq.com/s/mz__DiNxMGHwIKCLsjKR8g)
- [如何mock固定QPS的接口](https://mp.weixin.qq.com/s/yogj9Fni0KJkyQuKuDYlbA)
- [mock延迟响应的接口](https://mp.weixin.qq.com/s/x_fu0InQpYIUJIQFi9a50g)
- [moco固定QPS接口升级补偿机制](https://mp.weixin.qq.com/s/zAM91e_REo4edSPTLuHLOw)
## 工具类
- [java网格输出的类](https://mp.weixin.qq.com/s/BJTJu0LGjn7Hc9J1yT04KQ)
- [java使用poi写入excel文档的一种解决方案](https://mp.weixin.qq.com/s/Ft56gd1B9CPrQs2zq4Cpug)
- [java使用poi读取excel文档的一种解决方案](https://mp.weixin.qq.com/s/ltZGx9J7E8DTer0D-pfQ2Q)
- [MongoDB操作类封装](https://mp.weixin.qq.com/s/u-RHOE5XrjOEkelWIxdplw)
- [java网格输出的类](https://mp.weixin.qq.com/s/QW8nKM2Bz7C75fdkCzSbpw)
- [将json数据格式化输出到控制台](https://mp.weixin.qq.com/s/2IPwvh-33Ov2jBh0_L8shA)
- [利用反射根据方法名执行方法的使用示例](https://mp.weixin.qq.com/s/5ntwDo4ZVcTh1PmK4vkNfA)
- [解决统计出现次数问题的方法类](https://mp.weixin.qq.com/s/gqz4wuKkMWAOIQwMtiupnA)
- [java利用时间戳来获取UTC时间](https://mp.weixin.qq.com/s/wbDIrwDnxb9_XWkkmP3A_g)
- [如何遍历执行一个包里面每个类的用例方法](https://mp.weixin.qq.com/s/OJwCOHCJ4TalatsEWbtzIQ)
- [阿拉伯数字转成汉字](https://mp.weixin.qq.com/s/jNZXIvwMpdxt7jIAlVBgHg)
- [获取JVM转储文件的Java工具类](https://mp.weixin.qq.com/s/f_TlOb3m8MeR3argBmTzzA)
- [基于DOM的XML文件解析类](https://mp.weixin.qq.com/s/scRj7OAhvJYL3mx_hCFp4A)
- [XML文件解析实践(DOM解析)](https://mp.weixin.qq.com/s/V2DG3osaPNUJzFNDQgqM-w)
- [基于DOM4J的XML文件解析类](https://mp.weixin.qq.com/s/K5R7iMXouTn4g0p14T7iAQ)
- [将HTTP请求对象转成curl命令行](https://mp.weixin.qq.com/s/861uMAMMWtINjy4Z99WA6w)
## 构建工具
- [java和groovy混编的Maven项目如何用intellij打包执行jar包](https://mp.weixin.qq.com/s/bKexZXlONeo3r6FDhfMltQ)
- [window系统权限不足导致gradle构建失败的解决办法](https://mp.weixin.qq.com/s/dqiQvmVG1o6glU-pknLDwQ)
- [使用groovy脚本使gradle灵活加载本地jar包的两种方式](https://mp.weixin.qq.com/s/p3K3ZS7iOUeKO7E94gKFVg)
- [Java 8,Jenkins,Jacoco和Sonar进行持续集成](https://mp.weixin.qq.com/s/dOoXnKnWtQmmC5itClsl4g)
- [Gradle如何在任务失败后继续构建](https://mp.weixin.qq.com/s/GcXDzRN7cM_QQpt9ytqoKg)
- [Gradle+Groovy基础篇](https://mp.weixin.qq.com/s/c2j7G-PoNtAB3oYYDUhCGw)
- [Gradle+Groovy提高篇](https://mp.weixin.qq.com/s/yXmYj_1fynLkR0-5FV_Arw)
- [Maven进行增量构建](https://mp.weixin.qq.com/s/ThQ7j6TS93KJZFqlNx8IQg)
- [SonarQube8.3中的Maven项目的测试覆盖率报告](https://mp.weixin.qq.com/s/Xhp26jyE1c7Auielz48Llw)
## plotly可视化
- [MacOS使用pip安装pandas提示Cannot uninstall 'numpy'解决方案](https://mp.weixin.qq.com/s/fIqMAMXRQvf_vBtS5jDsyg)
- [Python使用plotly生成本地文件教程](https://mp.weixin.qq.com/s/4dJdIP-g3fF40vX7S31jNg)
- [Python2.7使用plotly绘制本地散点图和折线图实例](https://mp.weixin.qq.com/s/9QWrA0c-STmrmjSkBYWvbQ)
- [Python可视化工具plotly从数据库读取数据作图示例](https://mp.weixin.qq.com/s/EUtPidiz_r1rpQBH_kudbA)
- [利用Python+plotly制作接口请求时间的violin图表](https://mp.weixin.qq.com/s/3GdiLaiVRfkxwM3MOG-U8w)
- [Python+plotly生成本地饼状图实例](https://mp.weixin.qq.com/s/61Qz9Kz-4ruzC0OvIuElpA)
- [python plotly处理接口性能测试数据方法封装](https://mp.weixin.qq.com/s/NxVdvYlD7PheNCv8AMYqhg)
- [利用python+plotly 制作接口响应时间Distplot图表](https://mp.weixin.qq.com/s/yrcUW1fFC18newqHcxhVvw)
- [利用 python+plotly 制作Contour Plots模拟双波源干涉现象](https://mp.weixin.qq.com/s/vNW80BDeHsyjNQrnaBGk3Q)
- [利用 python+plotly 制作双波源干涉三维图像](https://mp.weixin.qq.com/s/KSeV8VvQXRIg-bnzYoa5qg)
- [python plotly制作接口响应耗时的时间序列表(Time Series )](https://mp.weixin.qq.com/s/U8chcVzCjGTdT3T_X5v4kw)
- [python使用plotly批量生成图表](https://mp.weixin.qq.com/s/l18WfWz-s6qQ1JKKuh_2AQ)
================================================
FILE: long/1
================================================
id=324,23,4,234,2,4,32,4,23
size1=9
size2=5
c=5,10,15
a=3,6,9
b=4,8,12
================================================
FILE: long/30
================================================
309
163
240
173
169
114
158
87
106
107
143
131
175
112
236
230
357
83
255
191
314
120
316
81
195
107
141
163
151
123
171
237
172
109
128
206
206
149
83
104
191
198
90
105
157
91
118
148
214
238
148
175
191
276
161
158
74
345
115
134
154
154
139
96
187
174
124
105
160
70
77
134
303
82
127
169
370
174
147
233
128
198
480
140
95
231
189
237
279
170
108
146
240
165
109
236
181
228
150
108
340
207
141
221
161
97
183
134
136
111
155
90
123
192
187
130
134
128
155
330
127
324
281
197
214
193
146
120
76
121
254
157
164
186
120
145
192
132
317
154
195
144
121
185
138
97
120
287
251
221
126
599
130
208
137
261
165
100
179
218
159
102
109
175
176
124
99
225
268
161
105
98
179
136
170
201
100
98
342
231
111
292
180
239
85
145
155
232
159
195
121
307
164
203
102
122
235
203
172
280
336
177
226
318
114
146
137
204
293
138
188
366
77
131
202
174
175
131
120
253
147
183
123
208
140
274
184
118
117
140
335
162
164
132
142
183
220
126
221
216
157
94
177
115
267
174
178
80
113
152
86
177
194
184
91
129
171
356
144
159
143
205
237
156
198
90
198
130
107
137
140
148
93
239
281
162
229
301
460
142
173
271
67
168
434
116
139
227
257
98
158
249
192
201
130
191
92
83
102
82
413
168
223
272
114
302
256
154
127
231
224
100
114
148
106
175
285
127
201
167
229
138
96
170
133
104
104
102
230
237
224
241
129
165
314
182
175
136
270
175
130
132
123
162
354
184
100
207
181
354
120
97
196
171
99
107
301
190
169
191
150
80
157
183
101
171
148
100
180
132
234
235
148
184
100
117
121
163
290
399
130
296
74
164
208
90
307
167
97
123
134
127
249
225
250
208
233
91
83
244
411
162
90
237
161
109
112
154
126
98
179
96
168
195
350
162
271
271
112
379
175
218
153
120
91
118
90
83
87
237
196
246
186
126
106
127
142
162
411
57
94
118
163
121
99
152
194
318
361
135
82
121
175
119
263
251
146
181
131
196
74
299
119
133
102
65
118
87
341
177
112
290
133
120
146
94
241
110
271
190
239
243
160
167
266
223
456
251
173
116
597
176
277
213
159
103
129
122
170
217
466
206
118
257
142
111
155
153
297
127
225
124
236
192
194
223
203
106
302
401
244
112
178
157
186
151
124
159
118
203
153
358
108
209
333
247
154
114
72
143
113
102
144
96
183
114
128
119
230
410
234
187
160
157
266
64
228
162
121
219
250
129
289
141
112
133
128
178
97
126
293
263
245
94
301
271
104
239
223
116
168
156
330
93
165
106
139
107
139
85
173
200
290
146
199
128
193
109
195
224
329
149
153
316
137
111
319
114
144
133
213
145
201
245
134
214
255
178
164
197
112
306
132
240
145
152
110
95
170
89
130
106
199
182
153
131
232
293
178
167
182
145
174
128
152
192
172
106
171
94
286
183
195
568
237
267
152
263
144
119
124
152
159
84
169
149
159
249
440
223
298
161
152
136
269
275
188
124
185
134
379
244
177
115
105
103
207
269
123
130
109
130
269
151
124
125
223
142
95
184
528
201
232
152
141
189
179
261
202
203
93
273
133
155
276
150
101
119
253
396
252
162
141
121
206
119
198
125
161
154
120
116
226
202
426
156
205
191
108
135
93
99
379
152
140
141
226
180
146
141
144
273
68
216
306
95
180
128
155
127
166
87
167
407
128
157
178
103
166
152
221
147
66
113
135
209
289
217
265
136
247
98
436
131
259
246
125
150
189
153
238
101
155
82
105
137
288
179
238
141
418
194
105
122
207
128
170
264
289
174
121
190
203
111
148
266
334
102
214
177
307
118
108
228
168
176
345
92
159
106
292
184
192
168
184
191
226
135
121
198
86
327
327
165
119
117
158
136
270
110
201
192
104
182
275
92
262
195
231
165
102
160
161
241
122
207
135
112
169
98
155
315
118
138
259
178
248
201
293
158
315
201
187
144
143
226
207
117
149
191
153
277
166
112
124
227
195
162
135
164
286
95
188
113
132
117
126
403
122
288
141
106
146
347
267
121
137
334
119
144
345
228
115
171
187
99
92
120
131
173
166
132
351
133
204
208
117
180
262
176
119
248
179
149
160
198
104
200
220
145
206
146
246
126
249
240
196
368
160
196
365
219
205
122
122
224
143
240
171
251
171
149
97
408
251
127
172
154
217
281
118
357
145
126
207
242
216
134
171
109
139
177
131
142
201
123
124
147
80
166
93
120
83
156
111
115
422
132
154
107
249
102
107
174
140
218
147
84
283
79
262
202
314
235
174
83
222
138
143
193
184
314
101
199
185
129
148
154
182
289
82
363
134
103
161
218
327
101
191
111
174
276
294
179
163
167
296
212
142
196
152
103
288
285
105
150
119
86
153
140
178
216
179
164
382
88
467
291
104
308
247
416
97
259
158
158
152
206
163
283
157
191
119
132
121
156
269
152
133
177
330
165
150
194
105
161
199
86
269
127
109
105
117
148
393
176
206
316
130
103
205
106
422
167
185
203
123
136
145
138
259
143
98
143
117
300
239
106
196
101
174
373
158
171
373
106
190
105
221
399
208
199
108
100
144
272
76
226
259
135
71
171
313
110
232
103
162
155
221
112
105
193
146
173
147
351
145
105
223
303
255
239
246
104
82
192
137
116
127
403
336
120
203
115
161
116
313
253
141
209
326
146
163
342
101
134
234
178
290
173
179
155
329
241
172
241
136
226
140
186
245
128
193
191
109
68
214
118
113
167
172
180
118
244
175
411
52
302
97
121
131
147
171
174
188
203
163
180
125
175
89
287
309
119
145
390
140
236
104
132
322
89
113
231
152
364
141
316
114
205
148
232
228
266
233
185
204
183
205
319
251
174
160
152
95
270
98
159
258
290
115
131
114
122
169
155
122
104
123
106
366
202
75
115
122
170
147
286
247
181
122
199
136
217
399
206
119
100
126
116
427
156
178
225
380
126
168
166
153
79
156
195
241
175
174
145
84
260
223
203
198
183
108
154
162
140
130
91
200
397
486
100
157
190
185
128
145
118
339
238
107
112
270
163
315
147
256
217
163
125
159
145
328
134
243
239
136
246
111
285
473
100
87
80
200
245
210
215
91
239
77
171
118
121
111
95
326
86
146
175
356
155
244
111
231
138
275
147
185
156
190
170
129
177
209
227
161
206
120
261
383
220
79
221
189
138
134
135
82
230
367
165
106
179
225
129
74
171
178
137
81
195
122
144
162
224
110
187
155
269
171
250
286
156
166
140
134
106
296
143
102
285
308
94
163
163
97
119
91
160
135
201
135
402
111
162
266
374
113
130
295
303
213
93
512
188
292
161
63
265
88
165
238
138
285
362
153
93
113
204
521
427
166
85
145
92
116
348
246
155
276
122
226
93
328
86
177
261
94
265
216
209
123
133
302
199
136
139
282
157
173
208
395
95
199
166
139
105
310
125
91
174
102
99
128
134
93
137
165
240
170
254
165
138
208
146
175
232
97
191
144
204
76
233
100
154
149
156
191
380
113
225
228
195
391
337
202
178
107
243
219
185
164
218
267
304
240
159
168
155
109
136
84
265
280
81
106
91
116
220
352
224
191
140
132
131
96
418
219
142
228
167
312
132
122
370
101
168
328
312
149
136
360
205
170
114
171
139
238
110
189
347
166
102
74
336
158
214
249
79
131
291
124
105
140
97
190
270
367
210
285
173
159
115
173
233
84
144
151
131
103
286
136
148
301
140
124
203
118
158
181
130
311
299
145
159
156
108
138
156
248
323
254
140
111
195
150
153
200
396
177
143
127
220
228
181
114
93
201
67
347
180
134
174
96
163
127
159
148
147
174
105
157
225
206
348
333
311
113
313
274
181
151
150
107
108
182
170
225
147
151
267
264
135
178
162
154
109
114
95
153
158
107
169
80
84
173
262
214
168
373
200
325
171
179
192
153
193
116
211
254
243
119
148
185
152
171
286
396
129
296
165
396
173
184
112
302
165
157
347
297
233
94
119
120
177
359
132
188
230
209
102
147
250
204
149
158
197
67
86
487
198
252
176
84
253
106
233
119
163
186
97
275
238
245
325
214
185
162
193
206
102
277
89
145
102
190
155
389
135
87
185
80
219
164
242
187
138
118
187
219
107
129
274
155
159
120
115
283
260
147
190
159
222
212
293
114
119
200
173
232
169
230
134
116
352
337
107
207
195
136
109
90
252
206
221
384
307
180
115
119
161
218
100
212
286
336
205
92
220
66
204
183
253
128
166
256
228
75
79
371
109
222
181
214
126
145
82
105
112
214
160
104
150
275
124
169
413
122
224
218
175
117
185
116
115
252
132
280
235
266
170
139
80
73
257
200
308
80
147
129
234
145
95
415
271
128
111
289
371
228
142
90
183
136
285
129
115
329
427
184
171
172
189
248
175
201
283
95
168
165
165
274
186
209
193
157
218
100
514
277
127
144
228
244
373
183
205
131
145
230
89
78
162
153
266
197
68
78
66
337
137
128
473
182
181
224
246
117
157
167
230
249
665
131
267
414
130
113
85
275
152
108
227
392
194
82
206
142
136
118
213
104
152
116
129
188
190
134
122
133
246
130
133
153
168
301
199
365
221
103
240
343
222
223
169
224
80
243
193
144
240
219
175
273
165
174
132
313
188
223
156
128
107
242
140
546
97
112
181
176
132
168
314
137
179
141
129
87
127
171
171
97
146
117
122
86
214
88
83
366
201
105
374
124
181
194
97
69
122
124
150
149
325
137
236
145
203
80
160
299
164
192
93
193
326
148
199
158
177
105
74
255
354
159
215
144
119
80
149
209
272
333
230
287
149
235
121
215
246
86
163
206
206
155
160
146
189
169
191
123
387
283
166
91
169
233
223
229
68
235
371
354
242
398
189
65
224
315
88
179
113
106
199
436
106
189
135
232
255
96
382
101
97
280
90
195
67
87
55
379
117
294
169
164
166
196
169
177
119
151
157
614
362
176
174
381
370
130
285
94
102
170
161
142
280
89
84
111
126
224
119
153
332
183
231
246
112
234
177
204
134
155
240
174
179
196
258
314
161
139
88
134
227
233
178
142
275
194
123
141
94
135
105
144
193
134
468
177
92
135
345
390
360
145
358
67
192
111
144
129
191
191
202
97
185
112
107
205
140
164
369
438
88
100
174
67
67
66
72
304
143
170
179
81
226
91
219
246
295
139
166
171
167
215
203
248
278
174
219
117
240
200
116
285
126
143
203
126
170
218
146
148
139
250
315
67
145
112
137
137
136
157
150
85
126
249
220
94
213
194
257
424
111
177
201
317
212
170
258
165
279
240
161
323
246
263
182
200
125
149
258
255
304
285
124
226
245
219
438
74
287
275
274
144
257
144
176
74
207
141
111
106
237
116
155
86
60
58
73
393
243
338
193
158
193
250
136
185
105
153
231
258
151
317
154
134
94
156
403
284
143
174
166
206
171
177
235
113
80
187
118
231
246
102
84
84
130
152
295
140
168
504
208
209
468
222
263
122
334
153
200
116
191
126
87
152
110
170
195
148
181
116
178
141
155
159
100
352
238
354
204
242
111
579
525
216
102
167
86
253
174
183
191
77
146
93
83
195
113
85
111
222
234
104
139
253
93
58
78
430
73
332
209
128
185
111
199
241
163
221
350
120
246
157
329
316
111
212
114
121
133
247
135
82
211
212
204
260
272
216
312
264
273
105
278
93
150
115
191
184
217
161
92
171
536
201
139
243
103
297
232
237
145
208
190
236
181
125
510
230
134
130
203
285
119
289
141
549
158
138
169
256
212
82
257
147
95
334
140
168
108
181
161
149
140
126
163
248
245
151
83
231
61
56
61
72
67
54
136
316
199
232
128
172
123
121
109
127
214
171
273
209
436
633
477
417
81
260
215
77
152
167
146
82
121
121
136
166
219
155
84
250
142
149
213
241
107
215
102
168
244
89
123
96
259
290
293
245
234
109
300
397
242
155
235
165
154
195
130
137
212
102
184
127
289
271
123
152
448
196
288
246
120
376
191
108
168
133
122
214
170
312
134
177
339
121
371
80
142
255
134
234
65
60
101
64
51
74
112
383
75
222
153
223
167
283
268
136
104
354
218
112
330
128
175
320
188
230
260
205
249
179
98
121
148
96
232
216
190
233
290
306
127
178
168
306
102
199
259
163
237
269
305
305
180
207
129
119
224
91
122
136
103
139
181
349
175
241
175
160
186
283
148
266
291
179
441
177
206
140
105
144
285
314
434
129
104
96
194
179
218
125
221
124
170
156
159
181
291
280
83
197
64
64
84
65
63
67
130
328
303
131
163
136
134
154
165
130
240
128
176
198
149
220
137
123
429
173
199
157
349
145
181
294
288
178
230
104
158
125
314
364
211
153
77
230
286
189
123
283
179
178
118
124
829
269
239
227
106
183
116
123
214
119
161
94
80
88
141
178
121
164
410
147
125
242
182
250
134
239
220
170
196
475
229
174
400
256
356
252
144
72
294
139
128
117
237
236
149
83
112
91
212
101
89
62
64
77
139
507
367
233
116
366
82
276
135
179
110
262
232
208
381
390
107
370
107
226
155
125
126
96
159
202
213
285
208
168
131
377
84
270
144
203
252
242
276
204
150
247
417
74
404
383
221
151
136
320
149
133
130
67
90
264
112
118
246
223
203
145
206
248
269
311
336
112
330
142
152
459
309
101
101
140
169
194
258
181
256
260
74
73
127
139
141
111
174
71
118
228
67
61
60
68
55
52
60
117
51
================================================
FILE: long/poster.markdown
================================================
# FunTester广告位分享
由于公众号资源有限,所以暂定每周一篇头条软文投放,为了方便特写此文档.
日期周一代表当周.
ps:有兴趣可以一起聊一聊文末和次条.
转载事宜直接微信联系
[原创汇总,每周更新](https://gitee.com/fanapi/tester/blob/okay/document/directory.markdown)
|日期(周一)|状态|代号|付款|
|----|----|----|-----|
|12.7|已预订| L+C|已付款|
|12.14|已预订|L|已付款|
|12.21|已预订|N|已付款|
|12.28|已预订|L|已付款|
|1.4|已预订|N|被鸽|
|1.4|已预订|C|未付款|
|1.11|已预订|M|被鸽|
|1.11|已预订|M|已付款|
|1.18|未预定|||
|1.25|未预定|||
|2.22|已预定|七七八十一|已付款|
|3.1|未预定|虚位以待||
|3.8|已预定|花卷|已付款|
|3.15|虚位以待||
|3.22|已预定|花卷|未付款
|3.29|未预定|虚位以待||
|4.5|未预定|虚位以待||
|4.12|已预订|花卷|未付款|
|4.20|已预订|花卷|未付款|
|4.26|未预定|虚位以待|未付款|
================================================
FILE: long/sql/performance.sql
================================================
/*
Navicat Premium Data Transfer
Source Server : test
Source Server Type : MySQL
Source Server Version : 50640
Source Host : 172.18.4.55:3306
Source Schema : okayapi
Target Server Type : MySQL
Target Server Version : 50640
File Encoding : 65001
Date: 02/01/2020 18:13:16
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for performance
-- ----------------------------
DROP TABLE IF EXISTS `performance`;
CREATE TABLE `performance` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`threads` int(4) DEFAULT NULL COMMENT '线程数',
`rt` int(5) DEFAULT NULL COMMENT '平均响应时间,ms',
`qps` double(10,4) DEFAULT NULL COMMENT 'QPS处理能力 /s',
`error` double(10,4) DEFAULT NULL COMMENT '错误率',
`fail` double(10,4) DEFAULT NULL COMMENT '失败率',
`des` varchar(1000) DEFAULT NULL COMMENT '任务描述',
`total` int(10) DEFAULT NULL COMMENT '总请求次数',
`start_time` timestamp NULL DEFAULT NULL,
`end_time` timestamp NULL DEFAULT NULL,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
================================================
FILE: long/sql/request.sql
================================================
/*
Navicat Premium Data Transfer
Source Server : test
Source Server Type : MySQL
Source Server Version : 50640
Source Host : 172.18.4.55:3306
Source Schema : okayapi
Target Server Type : MySQL
Target Server Version : 50640
File Encoding : 65001
Date: 02/01/2020 18:14:01
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for request
-- ----------------------------
DROP TABLE IF EXISTS `request`;
CREATE TABLE `request` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`type` varchar(6) DEFAULT NULL,
`method` varchar(6) DEFAULT NULL,
`domain` varchar(100) DEFAULT NULL,
`api` varchar(100) DEFAULT NULL,
`status` int(10) DEFAULT NULL,
`code` int(10) DEFAULT NULL,
`expend_time` double(10,2) DEFAULT NULL,
`data_size` int(6) DEFAULT NULL,
`local_ip` varchar(20) DEFAULT NULL,
`local_name` varchar(20) DEFAULT NULL,
`create_time` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=122167 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
================================================
FILE: readme.markdown
================================================
# 分支简介
该分支为主分支,其他分支停止更新.
> **FunTester**,[腾讯云年度作者,优秀讲师 | 腾讯云+社区权威认证](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw),非著名测试开发,欢迎关注。
## [GitHub地址](https://github.com/JunManYuanLong/FunTester)
联系地址:FunTester@88.com
# [**570+原创文章**](/document/directory.markdown)
# [**接口篇**](/document/api.markdown)
# [**基础篇**](/document/base.markdown)
# [**升级篇**](/document/update.markdown)
# [**7788篇**](/document/7788.markdown)
* 文章链接可能无法访问,各位看官移步GitHub地址即可.
[FunTester测试框架架构图](http://pic.automancloud.com/structure.html)
[FunTester测试项目架构图](http://pic.automancloud.com/project.html)


================================================
FILE: settings.gradle
================================================
rootProject.name = 'funtester'
================================================
FILE: src/main/groovy/com/funtester/base/bean/AbstractBean.groovy
================================================
package com.funtester.base.bean
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.funtester.config.Constant
import com.funtester.frame.Save
import com.funtester.frame.SourceCode
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
/**
* bean的基类
*/
abstract class AbstractBean extends Constant{
static final Logger logger = LogManager.getLogger(AbstractBean.class)
/**
* 将bean转化为json,为了进行数据处理和打印
*
* @return
*/
JSONObject toJson() {
JSONObject.parseObject(JSONObject.toJSONString(this))
}
/**
* 文本形式保存
*/
def save() {
Save.saveJson(this.toJson(), this.getClass().toString() + SourceCode.getMark());
}
/**
* 控制台打印,使用WARN记录,以便查看
*/
def print() {
logger.warn(this.getClass().toString() + ":" + this.toString());
}
def initFrom(String str) {
JSONObject.parseObject(str, this.getClass())
}
def initFrom(Object str) {
initFrom(JSON.toJSONString(str))
}
def copyFrom(AbstractBean source) {
JSON.parseObject(JSON.toJSONString(source), source.class)
}
def copyTo(AbstractBean target) {
JSON.parseObject(JSON.toJSONString(this, target.class))
}
/**
* 这里bean的属性必需是可以访问的,不然会返回空json串
* @return
*/
@Override
String toString() {
JSONObject.toJSONString(this)
}
@Override
protected Object clone() {
initFrom(this)
}
}
================================================
FILE: src/main/groovy/com/funtester/base/bean/PerformanceResultBean.groovy
================================================
package com.funtester.base.bean
import com.funtester.db.mysql.MySqlTest
import com.funtester.frame.Output
import com.funtester.utils.DecodeEncode
/**
* 性能测试结果集
*/
class PerformanceResultBean extends AbstractBean implements Serializable {
private static final long serialVersionUID = -1595942562342357L;
/**
* 测试用例描述
*/
String mark
/**
* 开始时间
*/
String startTime
/**
* 结束时间
*/
String endTime
/**
* 表格信息
*/
String table
/**
* 线程数
*/
int threads
/**
* 总请求次数
*/
int total
/**
* 平均响应时间
*/
int rt
/**
* 吞吐量,公式为QPS=Thead/avg(time)
*/
double qps
/**
* 通过QPS=count(r)/T公式计算得到的QPS,在固定QPS模式中,这个值来源于预设QPS
*/
double qps2
/**
* 理论误差,两种统计模式
*/
String deviation
/**
* 错误率
*/
double errorRate
/**
* 失败率
*/
double failRate
/**
* 执行总数
*/
int executeTotal
PerformanceResultBean(String mark, String startTime, String endTime, int threads, int total, int rt, double qps, double qps2, double errorRate, double failRate, int executeTotal, String table) {
this.mark = mark
this.startTime = startTime
this.endTime = endTime
this.threads = threads
this.total = total
this.rt = rt
this.qps = qps
this.qps2 = qps2
this.errorRate = errorRate
this.failRate = failRate
this.executeTotal = executeTotal
this.table = DecodeEncode.zipBase64(table)
this.deviation = com.funtester.frame.SourceCode.getPercent(Math.abs(qps - qps2) * 100 / Math.max(qps, qps2))
Output.output(this.toJson())
Output.output(table)
MySqlTest.savePerformanceBean(this)
}
}
================================================
FILE: src/main/groovy/com/funtester/base/bean/RecordBean.groovy
================================================
package com.funtester.base.bean
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
/**
* 测试记录的bean
*/
class RecordBean extends AbstractBean implements Serializable{
private static final long serialVersionUID = -159594234325649847L;
static Logger logger = LogManager.getLogger(RecordBean.class)
String domain;
String type;
String api;
long expend_time;
int data_size;
int status;
int code;
String method;
String local_ip;
String local_name;
String create_time;
static RecordBean get() {
new RecordBean()
}
RecordBean setDomain(String domain) {
this.domain = domain
this
}
RecordBean setType(String type) {
this.type = type
this
}
RecordBean setApi(String api) {
this.api = api
this
}
RecordBean setExpend_time(long expend_time) {
this.expend_time = expend_time
this
}
RecordBean setData_size(int data_size) {
this.data_size = data_size
this
}
RecordBean setStatus(int status) {
this.status = status
this
}
RecordBean setCode(int code) {
this.code = code
this
}
RecordBean setMethod(String method) {
this.method = method
this
}
RecordBean setLocal_ip(String local_ip) {
this.local_ip = local_ip
this
}
RecordBean setLocal_name(String local_name) {
this.local_name = local_name
this
}
RecordBean setCreate_time(String create_time) {
this.create_time = create_time
this
}
@Override
def print() {
logger.info "接口:{},响应时间{}", api, expend_time
}
}
================================================
FILE: src/main/groovy/com/funtester/base/bean/RequestInfo.groovy
================================================
package com.funtester.base.bean
import com.alibaba.fastjson.JSONObject
import com.funtester.base.interfaces.MarkRequest
import com.funtester.config.Constant
import com.funtester.config.RequestType
import com.funtester.config.SysInit
import org.apache.http.Header
import org.apache.http.HttpEntity
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.util.EntityUtils
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
/**
* 请求信息封装类
*/
class RequestInfo extends AbstractBean implements Serializable {
private static final long serialVersionUID = 5942566988949859847L;
private static Logger logger = LogManager.getLogger(RequestInfo.class)
/**
* 请求信息的标记字段,用于日志记录请求
*/
private static MarkRequest mark;
static void initMark(MarkRequest markRequest) {
mark = markRequest;
}
/**
* 接口地址
*/
String apiName
/**
* 请求的url
*/
String url
/**
* 请求的uri
*/
String uri
/**
* 方法,get/post
*/
RequestType method
/**
* 域名
*/
String host
/**
* 协议类型
*/
String type
/**
* 参数
*/
String params
/**
* host是否是黑名单
*/
boolean isBlack;
/**
* 所有的请求header,会去重
*/
JSONObject headers
/**
* 存一下
*/
HttpRequestBase request
/**
* 通过request获取请求的相关信息,并输出部分信息
*
* @param request
*/
RequestInfo(HttpRequestBase request) {
this.request = request
getRequestInfo()
}
/**
* 封装获取请求的各种信息的方法
*
* @param request 传入请求对象
* @return 返回一个map,包含api_name,host_name,type,method,params
*/
private void getRequestInfo() {
method = RequestType.getRequestType request.getMethod()
uri = request.getURI().toString()// 获取uri
getRequestUrl(uri)
String one = url.substring(url.indexOf("//") + 2)// 删除掉http://
apiName = one.substring(one.indexOf("/"))// 获取接口名
host = one.substring(0, one.indexOf("/"))// 获取host地址
isBlack = SysInit.isBlack(host)
type = url.substring(0, url.indexOf("//") - 1)// 获取协议类型
if (method == RequestType.GET) {
if (!uri.contains(UNKNOW)) return
params = uri.substring(uri.indexOf(UNKNOW) + 1)
} else if (method == RequestType.POST) {
getPostRequestParams(request)
}
List<Header> list = Arrays.asList(request.getAllHeaders())
headers = new JSONObject() {
{
list.each {
put(it.name, it.value)
}
}
}
}
/**
* 获取请求url,遇到get请求,先截取
*
* @param uri
*/
private void getRequestUrl(String uri) {
url = uri.contains(UNKNOW) ? uri.substring(0, uri.indexOf(UNKNOW)) : uri
}
/**
* 获取响应实体,post path,put方法适用
*
* @param request
*/
private void getPostRequestParams(HttpEntityEnclosingRequestBase request) {
HttpEntity entity = request.getEntity()// 获取实体
if (entity == null) return
try {
params = EntityUtils.toString(entity)// 解析实体
EntityUtils.consume(entity)// 确保实体消耗
} catch (Exception e) {
logger.warn("获取post请求参数时异常!")
params = "entity类型:" + entity.getClass()
}
}
boolean isBlack() {
isBlack
}
String mark() {
mark == null ? Constant.EMPTY : mark.mark(request)
}
@Override
String toString() {
this.toJson().toString()
}
}
================================================
FILE: src/main/groovy/com/funtester/base/bean/Result.groovy
================================================
package com.funtester.base.bean
import com.alibaba.fastjson.JSONObject
import com.funtester.base.interfaces.ReturnCode
import com.funtester.config.Constant
/**
* 通用的返回体
* 配合moco框架使用
* @param < T >
*/
class Result<T> extends AbstractBean implements Serializable{
private static final long serialVersionUID = -196371159847L;
/**
* code码
*/
int code
/**
* 返回信息
*/
T data
Result(int code, T data) {
this.code = code
this.data = data
}
/**
* 返回简单的响应
* @param c
*/
Result(ReturnCode errorCode) {
this(errorCode.getCode(), errorCode.getDesc())
}
def Result() {
}
/**
* 返回成功响应内容
* @param data
* @return
*/
static <T> Result<T> success(T data) {
new Result(0, data)
}
static Result success() {
new Result()
}
static Result build(ReturnCode errorCode) {
new Result(errorCode)
}
static Result build(int code, String msg) {
new Result(code, msg)
}
static Result build(List listData) {
success([list: listData] as JSONObject)
}
/**
* 返回通用失败的响应内容
* @param data
* @return
*/
static <T> Result<T> fail(T data) {
new Result<T>(Constant.TEST_ERROR_CODE, data)
}
static Result fail() {
new Result(Constant.TEST_ERROR_CODE)
}
static Result fail(ReturnCode errorCode) {
new Result(errorCode)
}
/**
* 是否成功响应
* @return
*/
boolean isSuccess() {
code == 0
}
}
================================================
FILE: src/main/groovy/com/funtester/base/bean/VerifyBean.groovy
================================================
package com.funtester.base.bean
import com.alibaba.fastjson.JSON
import com.funtester.base.exception.ParamException
import com.funtester.config.VerifyType
import com.funtester.utils.JsonUtil
import com.funtester.utils.Regex
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
/**
* 验证对象类
*/
class VerifyBean extends AbstractBean implements Serializable, Cloneable {
private static Logger logger = LogManager.getLogger(VerifyBean.class)
private static final long serialVersionUID = -1595942567071153982L;
VerifyType type
/**
* 验证语法
*/
String verify
/**
* 待验证内容
*/
String value
String des
boolean isVerify
boolean result;
VerifyBean(String verify, String value, String des) {
this.value = value
this.des = des
def split = verify.split(REG_PART, 2)
this.verify = split[1]
this.type = VerifyType.getRequestType(split[0])
}
/**
* 用于进行自身验证,会进行isverify和result记录
* @return
*/
boolean verify() {
if (isVerify && result) return result
isVerify = true
result = verify(value)
result
}
/**
* 用于进行对象外String验证,不会修改对象属性
* @param val
* @return
*/
boolean verify(String val) {
boolean res
try {
switch (type) {
case VerifyType.CONTAIN:
res = val.contains(verify)
return res
case VerifyType.REGEX:
res = Regex.isRegex(val, verify)
return res
case VerifyType.JSONPATH:
def split = verify.split(REG_PART, 2)
def path = split[0]
def v = split[1]
def instance = JsonUtil.getInstance(JSON.parseObject(val))
res = instance.getVerify(path).fit(v)
return res
case VerifyType.HANDLE:
def sp = verify.split(REG_PART, 2)
def path = sp[0]
def ve = sp[1]
def instance = JsonUtil.getInstance(JSON.parseObject(val))
res = instance.getVerify(path).fitFun(ve)
return res
default:
ParamException.fail("验证类型参数错误!")
}
} catch (Exception e) {
logger.warn("验证出现问题: {}", e.getMessage())
res = false
} finally {
/*这里Groovy可以这么写,但是Java不能这么写,因为需要有返回值*/
logger.info("verify对象 {} ,验证结果: {}", verify, res)
}
}
@Override
def print() {
logger.info("{} 验证结果: {}", des, result)
}
@Override
VerifyBean clone() {
new VerifyBean(this.verify, this.value, this.des)
}
}
================================================
FILE: src/main/groovy/com/funtester/base/constaint/CaseBase.java
================================================
package com.funtester.base.constaint;
import com.alibaba.fastjson.JSONObject;
import com.funtester.db.mysql.MySqlTest;
import com.funtester.frame.SourceCode;
/**
* 用例虚拟类
*/
public abstract class CaseBase extends SourceCode {
/**
* 保存测试用例的执行结果
*
* @param label 测试用例的标签
* @param result 测试用例结果
*/
public void saveResult(String label, JSONObject result) {
MySqlTest.saveTestResult(label, result);
}
/**
* 前置处理
*
* @return
*/
public abstract boolean before();
/**
* 后置处理
*
* @return
*/
public abstract boolean after();
/**
* 初始化
*
* @return
*/
public abstract boolean init();
}
================================================
FILE: src/main/groovy/com/funtester/base/constaint/FixedQpsThread.java
================================================
package com.funtester.base.constaint;
import com.funtester.base.interfaces.MarkThread;
import com.funtester.config.HttpClientConstant;
import com.funtester.frame.execute.FixedQpsConcurrent;
import com.funtester.utils.Time;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class FixedQpsThread<F> extends ThreadBase<F> {
private static Logger logger = LogManager.getLogger(FixedQpsThread.class);
public int qps;
/**
* 根据属性isTimesMode判断,次数或者时间(单位ms)
*/
public int limit;
public boolean isTimesMode;
public FixedQpsThread(F f, int limit, int qps, MarkThread markThread, boolean isTimesMode) {
this.limit = limit;
this.qps = qps;
this.mark = markThread;
this.f = f;
this.isTimesMode = isTimesMode;
}
protected FixedQpsThread() {
super();
}
@Override
public void run() {
try {
before();
threadmark = this.mark == null ? EMPTY : this.mark.mark(this);
long s = Time.getTimeStamp();
doing();
long e = Time.getTimeStamp();
FixedQpsConcurrent.executeTimes.getAndIncrement();
int diff = (int) (e - s);
FixedQpsConcurrent.allTimes.add(diff);
if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
FixedQpsConcurrent.marks.add(diff + CONNECTOR + threadmark + CONNECTOR + Time.getNow());
} catch (Exception e) {
FixedQpsConcurrent.errorTimes.getAndIncrement();
logger.warn("执行任务失败!,标记:{}", threadmark, e);
} finally {
after();
}
}
@Override
public void before() {
}
}
================================================
FILE: src/main/groovy/com/funtester/base/constaint/ThreadBase.java
================================================
package com.funtester.base.constaint;
import com.funtester.base.interfaces.MarkThread;
import com.funtester.frame.SourceCode;
import com.funtester.httpclient.FunLibrary;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
/**
* 多线程任务基类,可单独使用
*
* @param <T> 必需实现Serializable
*/
public abstract class ThreadBase<F> extends SourceCode implements Runnable, Serializable {
private static final long serialVersionUID = -1282879464717720145L;
/**
* 全局的时间终止开关,true表示终止,false表示不终止.
*/
private static boolean ABORT = false;
/**
* 线程的名字
*/
public String threadName;
/**
* 线程标记对象,用户标记请求或者单次执行任务的
*/
public String threadmark;
/**
* 错误数
* <p>这里注意使用{@link FunLibrary#getHttpResponse(org.apache.http.client.methods.HttpRequestBase)}方法获取响应的功能封装方法,即使报错也不会抛异常.这样会导致errorNum错误数为零</p>
*/
public int errorNum;
/**
* 执行数,一般与响应时间记录数量相同
*/
public int executeNum;
/**
* 计数锁
* <p>
* 会在concurrent类里面根据线程数自动设定
* </p>
*/
protected CountDownLatch countDownLatch;
/**
* 标记对象
*/
public MarkThread mark;
/**
* 用于设置访问资源,用于闭包中无法访问包外实例对象的情况,这里还有一个用处就是在标记线程对象的时候,用到了这个t(参数标记模式中)
*
* @since 2020年10月19日, 统一用来设置HTTPrequestbase对象.同样可以用于执行SQL和redis查询语句或者对象, 暂未使用dubbo尝试
*/
public F f;
protected ThreadBase() {
}
/**
* 记录所有超时的请求标记
*/
public List<String> marks = new ArrayList<>();
/**
* 用于存储请求耗时集合
* 2021年03月16日,将统计集合提取为对象属性,用于外部访问,可用于取样器实现
*/
public List<Integer> costs = new ArrayList<>();
/**
* 运行待测方法的之前的准备
*/
public void before() {
ABORT = false;
}
/**
* 待测方法
*
* @throws Exception 抛出异常后记录错误次数,一般在性能测试的时候重置重试控制器不再重试
*/
protected abstract void doing() throws Exception;
/**
* 运行待测方法后的处理
*/
protected void after() {
costs = new ArrayList<>();
marks = new ArrayList<>();
if (countDownLatch != null)
countDownLatch.countDown();
}
/**
* 设置计数器
*
* @param countDownLatch
*/
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
/**
* 拷贝对象方法,用于统计单一对象多线程调用时候的请求数和成功数,对于<T>的复杂情况,需要将T类型也重写clone方法
*
* <p>
* 此处若具体实现类而非虚拟类建议自己写clone方法,子类重写需注意{@link ThreadBase#initBase()}方法调用
* </p>
*
* @return
*/
@Override
public abstract ThreadBase clone();
/**
* 用于对象拷贝之后,清空存储列表
*/
public void initBase() {
this.costs = new ArrayList<>();
this.marks = new ArrayList<>();
}
/**
* 线程任务是否需要提前关闭,默认返回false
* <p>
* 一般用于单线程错误率过高的情况
* </p>
*
* @return
*/
public boolean status() {
return false;
}
/**
* Groovy乘法调用方法
*
* @param num
* @return
*/
public List<ThreadBase> multiply(int num) {
return range(num).mapToObj(x -> this.clone()).collect(Collectors.toList());
}
/**
* 用于在某些情况下提前终止测试
*/
public static void stop() {
ABORT = true;
}
/**
* true表示终止,false表示不终止.
*
* @return
*/
public static boolean needAbort() {
return ABORT;
}
}
================================================
FILE: src/main/groovy/com/funtester/base/constaint/ThreadLimitTimeCount.java
================================================
package com.funtester.base.constaint;
import com.funtester.base.interfaces.MarkThread;
import com.funtester.config.HttpClientConstant;
import com.funtester.frame.execute.Concurrent;
import com.funtester.httpclient.GCThread;
import com.funtester.utils.Time;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
/**
* 请求时间限制的多线程类,限制每个线程执行的时间
* <p>
* 通常在测试某项用例固定时间的场景下使用,可以提前终止测试用例
* </p>
*
* @param <T> 闭包参数传递使用,Groovy脚本会有一些兼容问题,部分对象需要tostring获取参数值
*/
public abstract class ThreadLimitTimeCount<F> extends ThreadBase<F> {
private static final long serialVersionUID = -7017995186493855741L;
private static final Logger logger = LogManager.getLogger(ThreadLimitTimeCount.class);
public List<String> marks = new ArrayList<>();
/**
* 任务请求执行时间,单位是ms秒
*/
public int time;
public ThreadLimitTimeCount(F f, int time, MarkThread markThread) {
this.time = time * 1000;
this.f = f;
this.mark = markThread;
}
protected ThreadLimitTimeCount() {
super();
}
@Override
public void run() {
try {
before();
long ss = Time.getTimeStamp();
while (true) {
try {
threadmark = mark == null ? EMPTY : this.mark.mark(this);
long s = Time.getTimeStamp();
doing();
long et = Time.getTimeStamp();
executeNum++;
int diff =(int) (et - s);
costs.add(diff);
if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
marks.add(diff + CONNECTOR + threadmark + CONNECTOR + Time.getNow());
if ((et - ss) > time || status() || ThreadBase.needAbort()) break;
} catch (Exception e) {
logger.warn("执行任务失败!", e);
logger.warn("执行失败对象的标记:{}", threadmark);
errorNum++;
}
}
long ee = Time.getTimeStamp();
logger.info("线程:{},执行次数:{}, 失败次数: {},总耗时: {} s", threadName, executeNum, errorNum, (ee - ss) / 1000.0);
Concurrent.allTimes.addAll(costs);
Concurrent.requestMark.addAll(marks);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
after();
}
}
public boolean status() {
return errorNum > 10;
}
/**
* 运行待测方法的之前的准备
*/
@Override
public void before() {
super.before();
}
@Override
protected void after() {
super.after();
GCThread.stop();
}
}
================================================
FILE: src/main/groovy/com/funtester/base/constaint/ThreadLimitTimesCount.java
================================================
package com.funtester.base.constaint;
import com.funtester.base.interfaces.MarkThread;
import com.funtester.config.HttpClientConstant;
import com.funtester.frame.execute.Concurrent;
import com.funtester.httpclient.GCThread;
import com.funtester.utils.Time;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 请求时间限制的多线程类,限制每个线程执行的次数
*
* <p>
* 通常在测试某项用例固定时间的场景下使用,可以提前终止测试用例
* </p>
*
* @param <F> 闭包参数传递使用,Groovy脚本会有一些兼容问题,部分对象需要tostring获取参数值
*/
public abstract class ThreadLimitTimesCount<F> extends ThreadBase<F> {
private static final long serialVersionUID = -4617192188292407063L;
private static final Logger logger = LogManager.getLogger(ThreadLimitTimesCount.class);
/**
* 任务请求执行次数
*/
public int times;
public ThreadLimitTimesCount(F f, int times, MarkThread markThread) {
this.times = times;
this.f = f;
this.mark = markThread;
}
protected ThreadLimitTimesCount() {
super();
}
@Override
public void run() {
try {
before();
long ss = Time.getTimeStamp();
for (int i = 0; i < times; i++) {
try {
threadmark = mark == null ? EMPTY : this.mark.mark(this);
long s = Time.getTimeStamp();
doing();
long e = Time.getTimeStamp();
executeNum++;
int diff =(int) (e - s);
costs.add(diff);
if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
marks.add(diff + CONNECTOR + threadmark + CONNECTOR + Time.getNow());
if (status() || ThreadBase.needAbort()) break;
} catch (Exception e) {
logger.warn("执行任务失败!", e);
logger.warn("执行失败对象的标记:{}", threadmark);
errorNum++;
}
}
long ee = Time.getTimeStamp();
logger.info("线程:{},执行次数:{},错误次数: {},总耗时:{} s", threadName, times, errorNum, (ee - ss) / 1000.0);
Concurrent.allTimes.addAll(costs);
Concurrent.requestMark.addAll(marks);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
after();
}
}
/**
* 运行待测方法的之前的准备
*/
@Override
public void before() {
super.before();
}
@Override
public boolean status() {
return errorNum > 10;
}
@Override
protected void after() {
super.after();
GCThread.stop();
}
}
================================================
FILE: src/main/groovy/com/funtester/base/exception/FailException.java
================================================
package com.funtester.base.exception;
import com.funtester.config.Constant;
/**
* 自定义异常基类
*/
public class FailException extends RuntimeException {
private static final long serialVersionUID = -7041169491254546905L;
public FailException() {
super(Constant.DEFAULT_STRING);
}
protected FailException(String message) {
super(message);
}
public static void fail(String message) {
throw new FailException(message);
}
/**
* 默认抛异常,多用于调试
*/
public static void fail() {
throw new FailException();
}
/**
* 将检查异常修改为运行异常
*
* @param e
*/
public static void fail(Exception e) {
throw new FailException(e.getMessage());
}
}
================================================
FILE: src/main/groovy/com/funtester/base/exception/LoginException.java
================================================
package com.funtester.base.exception;
import com.alibaba.fastjson.JSONObject;
/**
* 处理项目中的登录异常
*/
public class LoginException extends FailException {
private static final long serialVersionUID = 8674617502387938483L;
private LoginException() {
super();
}
private LoginException(String message) {
super(message);
}
public static void fail(String message) {
throw new LoginException(message);
}
/**
* 用于处理记录登录响应结果的抛异常方法
*
* @param response 登录接口响应结果
*/
public static void fail(JSONObject response) {
fail(response.toJSONString());
}
}
================================================
FILE: src/main/groovy/com/funtester/base/exception/ParamException.java
================================================
package com.funtester.base.exception;
import com.alibaba.fastjson.JSONObject;
/**
* 参数错误运行异常类
*/
public class ParamException extends FailException {
private static final long serialVersionUID = -5079364420579956243L;
private ParamException() {
super();
}
private ParamException(String message) {
super(message);
}
public static void fail(String message) {
throw new ParamException(message);
}
public static void fail(JSONObject message) {
throw new ParamException(message.toJSONString());
}
}
================================================
FILE: src/main/groovy/com/funtester/base/exception/RequestException.java
================================================
package com.funtester.base.exception;
import org.apache.http.client.methods.HttpRequestBase;
/**
* 用于处理请求异常
*/
public class RequestException extends FailException {
private static final long serialVersionUID = 7916010541762451964L;
private RequestException() {
super();
}
private RequestException(HttpRequestBase request) {
super(request.toString());
}
public RequestException(String message) {
super(message);
}
public static void fail(HttpRequestBase base) {
throw new RequestException(base);
}
public static void fail(String message) {
throw new RequestException(message);
}
}
================================================
FILE: src/main/groovy/com/funtester/base/exception/VerifyException.java
================================================
package com.funtester.base.exception;
import com.alibaba.fastjson.JSONObject;
import com.funtester.frame.SourceCode;
import com.funtester.httpclient.FunRequest;
import org.apache.http.client.methods.HttpRequestBase;
/**
* 用于处理验证过程中的异常
*/
public class VerifyException extends FailException {
private static final long serialVersionUID = 7916010541762451964L;
private VerifyException() {
super();
}
private VerifyException(HttpRequestBase request) {
super(request.toString());
}
private VerifyException(String message) {
super(message);
}
public static void fail(String message) {
SourceCode.getiMessage().sendBusinessMessage();
throw new VerifyException(message);
}
public static void fail(JSONObject message) {
SourceCode.getiMessage().sendBusinessMessage();
fail(message.toJSONString());
}
public static void fail(HttpRequestBase request) {
SourceCode.getiMessage().sendBusinessMessage();
fail(FunRequest.initFromRequest(request).toString());
}
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/IBase.java
================================================
package com.funtester.base.interfaces;
import com.alibaba.fastjson.JSONObject;
import com.funtester.base.bean.RequestInfo;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import java.io.File;
/**
* 每个项目需要重写的方法
*/
public interface IBase {
/**
* 获取get请求对象
*
* @param url
* @return
*/
HttpGet getGet(String url);
/**
* 获取get请求对象
*
* @param url
* @param arg
* @return
*/
HttpGet getGet(String url, JSONObject arg);
/**
* 获取post请求对象
*
* @param url
* @return
*/
HttpPost getPost(String url);
/**
* 获取post请求对象
*
* @param url
* @param params
* @return
*/
HttpPost getPost(String url, JSONObject params);
/**
* 获取post请求对象
*
* @param url
* @param params
* @param file
* @return
*/
HttpPost getPost(String url, JSONObject params, File file);
/**
* 获取响应
*
* @param request
* @return
*/
JSONObject getResponse(HttpRequestBase request);
/**
* 获取响应
*
* @param url
* @return
*/
JSONObject getGetResponse(String url);
/**
* 获取响应
*
* @param url
* @param args
* @return
*/
JSONObject getGetResponse(String url, JSONObject args);
/**
* 获取响应
*
* @param url
* @return
*/
JSONObject getPostResponse(String url);
/**
* 获取响应
*
* @param url
* @param params
* @return
*/
JSONObject getPostResponse(String url, JSONObject params);
/**
* 获取响应
*
* @param url
* @param params
* @param file
* @return
*/
JSONObject getPostResponse(String url, JSONObject params, File file);
/**
* 校验响应正确性
* <p>
* 用于处理响应结果,一般校验json的必要层级和响应码
* </p>
*
* @param response
* @return
*/
boolean isRight(JSONObject response);
/**
* 检查响应是否符合标准
* <p>
* 会在FunLibrary类使用,如果没有ibase对象,会默认返回test_error_code
* requestinfo主要用于校验该请求是否需要校验,黑名单有配置black_host提供
* </p>
*
* @param response 响应json
* @param requestInfo 请求info
* @return
*/
int checkCode(JSONObject response, RequestInfo requestInfo);
/**
* 登录
*/
void login();
/**
* 设置header
*/
void setHeaders(HttpRequestBase request);
/**
* 处理响应结果
*
* @param response
*/
void handleResponseHeader(JSONObject response);
/**
* 获取公共的登录参数
*
* @return
*/
JSONObject getParams();
/**
* 初始化对象,从json数据中,一般指cookie
* <p>
* 主要用于new了新的对象之后,然后赋值的操作,场景是从另外一个服务的对象拷贝到现在的对象,区别于clone,因为可能还会涉及其他的验证,所以单独写出一个方法,极少用到
* </p>
*/
void init(JSONObject info);
/**
* 记录请求
*/
void recordRequest(HttpRequestBase base);
/**
* 获取请求,用于并发
*
* @return
*/
HttpRequestBase getRequest();
/**
* 输出JSON格式的响应结果,用于统一屏蔽打印或者不打印响应内容
*
* @param response
*/
public void print(JSONObject response);
/**
* 打印所有的请求header,此处功能与print响应类似,需要用一个开关控制
*
* @param request
*/
public void printHeader(HttpRequestBase request);
/**
* 打印所有的响应header,此处功能与print响应类似,需要用一个开关控制
*
* @param response
*/
public void printHeader(CloseableHttpResponse response);
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/IMessage.java
================================================
package com.funtester.base.interfaces;
public interface IMessage {
/**
* 发送系统异常
*/
public void sendSystemMessage();
/**
* 发送功能异常
*/
public void sendFunctionMessage();
/**
* 发送业务异常
*/
public void sendBusinessMessage();
/**
* 发送程序异常
*/
public void sendCodeMessage();
/**
* 提醒推送
*/
public void sendRemindMessage();
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/IMySqlBasic.java
================================================
package com.funtester.base.interfaces;
import java.sql.ResultSet;
/**
* 项目数据库执行类接口
*/
public interface IMySqlBasic {
/**
* 执行查询sql
*
* @param sql
* @return
*/
ResultSet executeQuerySql(String sql);
/**
* 执行查询sql
*
* @param database
* @param sql
* @return
*/
ResultSet executeQuerySql(String database, String sql);
/**
* 执行修改sql
*
* @param sql
*/
void executeUpdateSql(String sql);
/**
* 执行查询sql
*
* @param database
* @param sql
*/
void executeUpdateSql(String database, String sql);
/**
* 关闭数据库连接
*/
void mySqlOver();
/**
* 初始化数据库连接
*
* @param database
*/
void getConnection(String database);
/**
* 初始化数据库连接
*/
void getConnection();
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/ISocketClient.java
================================================
package com.funtester.base.interfaces;
import com.alibaba.fastjson.JSONObject;
import com.funtester.socket.ScoketIOFunClient;
import com.funtester.socket.WebSocketFunClient;
import java.util.List;
/**
* 对于基类base拓展Socket功能,暂时分成WebSocket和Socket.IO
* {@link ScoketIOFunClient}
* {@link WebSocketFunClient}
*/
public interface ISocketClient {
/**
* 连接
*/
void connect();
/**
* 初始化
*/
void init();
/**
* 发送消息
*
* @param mgs
*/
void send(JSONObject mgs);
/**
* 发送消息
*
* @param mgs
*/
void send(String mgs);
/**
* 关闭
*/
void close();
/**
* 克隆对象,性能测试中需要
*/
ISocketClient clone();
/**
* 是否已连接
*
* @return
*/
boolean isConnect();
/**
* 获取记录的消息,用于验证响应,请注意需要返回副本
*
* @return
*/
List<String> getMsgs();
/**
* 用于保存收到的信息,不同于Client的saveMsg,此方法需要将对象存储的消息全都存到long_path目录下,是否需要清空Client对象中的msgs信息,需要视情况而定.
*/
void savaMsg();
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/ISocketVerify.java
================================================
package com.funtester.base.interfaces;
import com.funtester.base.bean.VerifyBean;
import java.util.List;
/**
* Socket接口通用验证接口,暂时无用
*/
public interface ISocketVerify extends Runnable {
/**
* 初始化消息,某些场景下需要将消息转成固定对象,进行验证,如json
*
* @param msg
*/
public void initMsg(List<String> msg);
/**
* 执行一次现有消息的全部验证,是否有匹配
*
* @return
*/
public boolean verify();
/**
* 往验证队列中添加verify对象
*
* @param bean
*/
public void addVerify(VerifyBean bean);
/**
* 清除verify,验证通过的verify可以从队列中清除
*
* @param bean
*/
public void remoreVerify(VerifyBean bean);
/**
* 清除所有验证对象,通常是未验证通过,可以区分未通过和已通过
*/
public void removeAllVerify();
/**
* 保存verify队列的测试结果
*/
public void saveResult();
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/MarkRequest.java
================================================
package com.funtester.base.interfaces;
import org.apache.http.client.methods.HttpRequestBase;
/**
* 专门用来标记HTTP请求的接口
*/
public interface MarkRequest extends MarkThread {
/**
* 标记请求对象
*
* @param requestBase
* @return
*/
public String mark(HttpRequestBase requestBase);
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/MarkThread.java
================================================
package com.funtester.base.interfaces;
import com.funtester.base.constaint.ThreadBase;
/**
* 用来标记thread,为了记录超时的请求
*/
public interface MarkThread {
/**
* 标记线程任务
*
* @param threadBase
* @return
*/
public String mark(ThreadBase threadBase);
public MarkThread clone();
}
================================================
FILE: src/main/groovy/com/funtester/base/interfaces/ReturnCode.java
================================================
package com.funtester.base.interfaces;
public interface ReturnCode {
int getCode();
String getDesc();
}
================================================
FILE: src/main/groovy/com/funtester/config/Constant.java
================================================
package com.funtester.config;
import com.funtester.utils.FileUtil;
import com.funtester.utils.Time;
import org.apache.http.Consts;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;
/**
* 常量类
*/
public class Constant {
private static Logger logger = LogManager.getLogger(Constant.class);
/*常用的常量*/
public static final String LINE = "\r\n";
public static final String TAB = "\t";
public static final String EMPTY = "";
public static final String COMMA = ",";
public static final String UNKNOW = "?";
public static final String OR = "/";
public static final String PART = "|";
/**
* 正则表达式中用到的{@link Constant#PART}
*/
public static final String REG_PART = "\\|";
public static final String SPACE_1 = " ";
public static final String CONNECTOR = "_";
public static final String QUOTE_DOUBLE = "\"";
public static final String QUOTE_SINGLE = "\'";
private static final String[] PERCENT = {SPACE_1, "▁", "▂", "▃", "▄", "▅", "▅", "▇", "█"};
/**
* 此处前七处等高,第八个元素不等高,不能正常使用
*/
private static final String[] PARTS = {SPACE_1, "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"};
/**
* 统计性能数据的分桶数
*/
public static final int BUCKET_SIZE = 32;
/**
* 读写配置文件过滤的文本
*/
public static final String FILTER = "##";
public static final String DEFAULT_STRING = "FunTester";
public static final String RESPONSE_CODE = "code";
public static final String RESPONSE_CONTENT = "content";
public static final int TEST_ERROR_CODE = -2;
public static final long DEFAULT_LONG = 0L;
public static final Properties SYSTEM_INFO = getSysInfo();
private static Properties getSysInfo() {
return System.getProperties();
}
/**
* 校验IP+port的正确性
*/
public static final String HOST_REGEX = "((25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))):([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])";
/**
* UTF-8字符编码格式
*/
public static final Charset UTF_8 = Consts.UTF_8;
/**
* gb2312编码格式
*/
public static final Charset GB2312 = Charset.forName("gb2312");
/**
* Unicode编码格式
*/
public static final Charset UNICODE = Charset.forName("Unicode");
/**
* utf-16字符集
*/
public static final Charset UTF_16 = Charset.forName("UTF-16");
/**
* ISO-8859-1编码格式
*/
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
/**
* GBK编码格式
*/
public static final Charset GBK = Charset.forName("GBK");
/**
* 默认字符集
*/
public static Charset DEFAULT_CHARSET = UTF_8;
/**
* 当前工作目录
*/
public static final String WORK_SPACE = new File(EMPTY).getAbsolutePath() + "/";
/**
* 测试数据存储目录
*/
public static final String LONG_Path = WORK_SPACE + "long/";
/**
* 日志存存储目录
*/
public static final String LOG_Path = WORK_SPACE + "log/";
/**
* request日志记录目录
*/
public static final String REQUEST_Path = LONG_Path + "request/";
/**
* 标记请求地址
*/
public static final String MARK_Path = LONG_Path + "mark/";
/**
* 压测数据存放地址
*/
public static final String DATA_Path = LONG_Path + "data/";
/**
* 毫秒数
*/
public static final long DAY = 86400000;
/**
* 反射方法执行用例时间间隔
*/
public static final int EXECUTE_GAP_TIME = 10;
/**
* 本机ip,程序初始化会赋值
*/
public static final String LOCAL_IP = getLocalIp();
/**
* 本机用户名,程序初始化会赋值
*/
public static final String COMPUTER_USER_NAME = SYSTEM_INFO.getOrDefault("user.name", DEFAULT_STRING).toString();
public static final String JAVA_VERSION = SYSTEM_INFO.get("java.version").toString();
public static final String SYS_ENCODING = SYSTEM_INFO.get("file.encoding").toString();
public static final String SYS_VERSION = SYSTEM_INFO.get("os.version").toString();
public static final String SYS_NAME = SYSTEM_INFO.get("os.name").toString();
/**
* 获取本机IP
*
* @return
*/
public static String getLocalIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.warn("获取本机IP失败!", e);
return EMPTY;
}
}
/**
* 直接获取long目录下的文件
*
* @param fileName
* @return
*/
public static String getLongFile(String fileName) {
return LONG_Path + fileName;
}
public static String getPercent(int i) {
return PERCENT[i % 9];
}
public static String getPart(int i) {
return PARTS[i % 9];
}
/**
* 创建日志文件夹和数据存储文件夹
*/
static {
new File(LOG_Path).mkdir();
new File(LONG_Path).mkdir();
File file = new File(REQUEST_Path);
File mark = new File(MARK_Path);
File data = new File(DATA_Path);
file.mkdir();
mark.mkdir();
data.mkdir();
List<String> allFile = FileUtil.getAllFile(DATA_Path);
allFile.addAll(FileUtil.getAllFile(MARK_Path));
allFile.addAll(FileUtil.getAllFile(REQUEST_Path));
allFile.stream().map(y -> new File(y)).forEach(x -> {
if (Time.getTimeStamp() - x.lastModified() > 3 * DAY) x.delete();
});
logger.info("当前用户:{},IP:{},工作目录:{},系统编码格式:{},系统{}版本:{}", COMPUTER_USER_NAME, LOCAL_IP, WORK_SPACE, SYS_ENCODING, SYS_NAME, SYS_VERSION);
}
}
================================================
FILE: src/main/groovy/com/funtester/config/EmailConstant.java
================================================
package com.funtester.config;
public class EmailConstant {
/**
* QQ邮箱发件服务器
*/
public static final String QQ_HOST = "smtp.qq.com";
/**
* QQ发件邮箱
*/
public static final String QQ_USERNAME = "1009329307@qq.com";
/**
* 授权码
*/
public static final String QQ_PASSWORD = "nhkmsrcucjpgbbcj";
/**
* email加密传输类型
*/
public static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
}
================================================
FILE: src/main/groovy/com/funtester/config/HttpClientConstant.java
================================================
package com.funtester.config;
import org.apache.http.Header;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.funtester.config.Constant.DEFAULT_CHARSET;
import static com.funtester.httpclient.FunLibrary.getHeader;
/**
*
*/
public class HttpClientConstant {
static PropertyUtils.Property propertyUtils = PropertyUtils.getProperties("http");
static String getProperty(String name) {
return propertyUtils.getProperty(name);
}
/**
* 默认user_agent
*/
public static Header USER_AGENT = getHeader("User-Agent", getProperty("User-Agent"));
/**
* 从连接目标url最大超时 单位:毫秒
*/
public static int CONNECT_REQUEST_TIMEOUT = propertyUtils.getPropertyInt("TIMEOUT") * 1000;
/**
* 连接池中获取可用连接最大超时时间 单位:毫秒
*/
public static int CONNECT_TIMEOUT = CONNECT_REQUEST_TIMEOUT;
/**
* 等待响应(读数据)最大超时 单位:毫秒
*/
public static int SOCKET_TIMEOUT = CONNECT_REQUEST_TIMEOUT;
/**
* 记录
*/
public static int MAX_ACCEPT_TIME = propertyUtils.getPropertyInt("MAX_ACCEPT_TIME") * 1000;
/**
* 连接池最大连接数
*/
public static int MAX_TOTAL_CONNECTION = 5000;
/**
* 每个路由最大连接数
*/
public static int MAX_PER_ROUTE_CONNECTION = 2000;
/**
* 最大header数
*/
public static int MAX_HEADER_COUNT = 100;
/**
* 消息最大长度
*/
public static int MAX_LINE_LENGTH = 10000;
/**
* 设置的本机ip
*/
public static String IP = SysInit.getRandomIP();
/**
* 连接header设置
*/
public static Header CONNECTION = getHeader("Connection", getProperty("Connection"));
public static Header CLIENT_IP = getHeader("Client-Ip", IP);
public static Header HTTP_X_FORWARDED_FOR = getHeader("HTTP_X_FORWARDED_FOR", IP);
public static Header WL_Proxy_Client_IP = getHeader("WL-Proxy-Client-IP", IP);
public static Header Proxy_Client_IP = getHeader("Proxy-Client-IP", IP);
public static Header X_FORWARDED_FOR = getHeader("X-FORWARDED-FOR", IP);
public static Header ContentType_JSON = getHeader("Content-Type", "application/json; charset=" + DEFAULT_CHARSET.toString());
public static Header ContentType_FORM = getHeader("Content-Type", "application/x-www-form-urlencoded; charset=" + DEFAULT_CHARSET.toString());
public static Header ContentType_TEXT = getHeader("Content-Type", "text/plain; charset=" + DEFAULT_CHARSET.toString());
public static Header X_Requested_KWith = getHeader("X-Requested-With", "XMLHttpRequest");
/**
* 重试次数
*/
public static int TRY_TIMES = propertyUtils.getPropertyInt("TRY_TIMES");
/**
* 关闭超时的链接
*/
public static int IDLE_TIMEOUT = 5;
/**
* 在设置请求contenttype参数,表示请求以io流发送数据
*/
public static String CONTENTTYPE_MULTIPART_FORM = "multipart/form-data";
/**
* 在设置请求contenttype参数,表示请求以文本发送数据
*/
public static String CONTENTTYPE_TEXT = "text/plain";
/**
* 请求头,cookie
*/
public static String COOKIE = "cookie";
/**
* SSL版本
*/
public static String SSL_VERSION = getProperty("ssl_v");
/**
* 域名黑名单
*/
public static List<String> BLACK_HOSTS = new ArrayList<>();
/**
* 通用循环间隔时间,单位s
*/
public static final int LOOP_INTERVAL = 5;
/**
* 线程池,线程最大空闲时间
*/
public static final int THREAD_ALIVE_TIME = 3;
/**
* 线程池核心线程数
*/
public static final int THREADPOOL_CORE = 20;
/**
* 线程池最大线程数
*/
public static final int THREADPOOL_MAX = 500;
/**
* 关闭线程池最大等待时间
*/
public static final int WAIT_TERMINATION_TIMEOUT = 10;
/**
* 添加黑名单
*
* @param host
*/
public static void addBlackHost(String host) {
BLACK_HOSTS.add(host);
}
static {
BLACK_HOSTS.addAll(Arrays.asList(getProperty("black_host").split(",")));
}
}
================================================
FILE: src/main/groovy/com/funtester/config/PropertyUtils.groovy
================================================
package com.funtester.config
import com.alibaba.fastjson.JSONObject
import com.funtester.utils.RWUtil
import com.funtester.frame.SourceCode
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.util.stream.Stream
/**
* 读取配置工具
*/
class PropertyUtils extends SourceCode {
private static Logger logger = LogManager.getLogger(PropertyUtils.class)
/**
* 获取指定.properties配置文件中所以的数据
* @param propertyName
* 调用方式:
* 1.配置文件放在resource源包下,不用加后缀
* PropertiesUtil.getAllMessage("message")
* 2.放在包里面的
* PropertiesUtil.getAllMessage("com.test.message")
* @return
*/
static Property getProperties(String propertyName) {
logger.debug("读取配置文件:{}", propertyName)
try {
new Property(ResourceBundle.getBundle(propertyName.trim()))
} catch (MissingResourceException e) {
getLocalProperties(WORK_SPACE + propertyName + ".properties")
}
}
/**
* 获取指定路径下的文件配置,过滤掉{@link com.funtester.config.Constant#FILTER}
* @param filePath
* @return
*/
static Property getLocalProperties(String filePath) {
logger.debug("读取配置文件:{}", filePath)
try {
new Property(RWUtil.readTxtByJson(filePath, FILTER))
} catch (MissingResourceException e) {
logger.warn("找不到配置文件", e)
new Property()
}
}
/**
* 获取当前项目下的文件配置,过滤掉{@link com.funtester.config.Constant#FILTER}
* @param propertyName
* @return
*/
static Property getPropertiesByFile(String propertyName) {
getLocalProperties(WORK_SPACE + propertyName)
}
/**
* 配置项
*/
static class Property {
Map<String, String> properties = new HashMap<>()
def Property(ResourceBundle resourceBundle) {
def set = resourceBundle.keySet()
for (def key in set) {
properties.put key, resourceBundle.getString(key)
}
}
def Property(JSONObject json) {
properties.putAll(json)
}
/**
* 获取string类型
* @param name
* @return
*/
String getProperty(String name) {
PropertyUtils.logger.debug("获取配置项:{}", name)
if (contain(name)) properties.get(name)
}
/**
* 获取int值
* @param name
* @return
*/
int getPropertyInt(String name) {
changeStringToInt(properties.get(name))
}
/**
* 获取long值
* @param name
* @return
*/
int getPropertyLong(String name) {
Long.valueOf(properties.get(name))
}
/**
* 获取boolean值
* @param name
* @return
*/
boolean getPropertyBoolean(String name) {
changeStringToBoolean(properties.get(name))
}
/**
* 获取数组
* @param name
* @return
*/
String[] getArrays(String name) {
getProperty(name).split(COMMA)
}
/**
* 获取数字类型的数组
* @param name
* @return
*/
Integer[] getIntArray(String name) {
def split = getProperty(name).split(COMMA)
Stream.of(split).map { x -> x as Integer }.toArray()
}
/**
* 返回配置文件的配置项的大小
* @return
*/
int size() {
properties.size()
}
/**
* 输出所以配置项
* @return
*/
def printAll() {
output properties
}
/**
* 是否有配置项
* @param key
* @return
*/
boolean contain(def key) {
boolean var = properties.containsKey key asBoolean()
if (!var) PropertyUtils.logger.error("配置{}未发现!", key)
var
}
}
}
================================================
FILE: src/main/groovy/com/funtester/config/RequestType.java
================================================
package com.funtester.config;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 请求枚举类,fun备用,暂时无用,通过其他方式区分了post请求的参数格式
*/
public enum RequestType {
GET("get"), POST("post"), FUN("fun");
static Logger logger = LogManager.getLogger(RequestType.class);
String name;
private RequestType(String name) {
this.name = name;
}
public static RequestType getRequestType(String name) {
logger.debug("验证请求方式:{}", name);
for (RequestType requestType : RequestType.values()) {
if (requestType.name.equalsIgnoreCase(name)) {
return requestType;
}
}
return FUN;
}
/**
* 获取名字
*
* @return
*/
public String getName() {
return name;
}
}
================================================
FILE: src/main/groovy/com/funtester/config/SocketConstant.java
================================================
package com.funtester.config;
/**
* socket测试相关的配置
*/
public class SocketConstant {
/* WebSocket独享配置 */
/**
* 最大等待次数,超过次数*时间就是连接失败
*/
public static int MAX_WATI_TIMES = 3;
/* 共享配置 */
/**
* 默认连接间隔
*/
public static int WAIT_INTERVAL = 3;
/**
* 默认最大的保存响应消息的数量
*/
public static int MAX_MSG_SIZE = 200;
/*Socket.IO独享配置*/
/**
* Socket.IO独享配置
*/
public static int MAX_RETRY = 3;
/**
* 重试延迟
*/
public static int RETRY_DELAY = 1000;
/**
* 请求超时
*/
public static int TIMEOUT = 10000;
public static String[] transports = {"websocket"};
}
================================================
FILE: src/main/groovy/com/funtester/config/SqlConstant.java
================================================
package com.funtester.config;
/**
*
*/
public class SqlConstant {
static PropertyUtils.Property propertyUtils = PropertyUtils.getProperties("mysql");
static String getProperty(String name) {
return propertyUtils.getProperty(name);
}
/**
* 驱动名称
*/
public static final String DRIVE = "com.mysql.cj.jdbc.Driver";
/**
* 数据库默认连接设置
*/
public static final String SQLARGS = "?useUnicode=true&characterEncoding=utf-8&useOldAliasMetadataBehavior=true";
/**
* 测试数据库
*/
public static final String TEST_SQL_URL = getProperty("test_mysql_url") + SQLARGS;
public static final String TEST_USER = getProperty("user");
public static final String TEST_PASS_WORD = getProperty("password");
/**
* 数据库账号
*/
public static final String FUN_SQL_URL = "jdbc:mysql://ip/database" + SQLARGS;
/**
* 数据库存储服务接口地址
*/
public static final String MYSQL_SERVER_PATH = getProperty("mysql_server_path");
/**
* 数据库连接重连间隔
*/
public static final int MYSQL_RECONNECTION_GAP = 250;
/**
* 数据库存储任务每个线程最大等待数量
*/
public static final int MYSQL_WORK_PER_THREAD = 30;
/**
* 最大等待数量,超过上限不再创建新的线程
*/
public static final int MYSQL_MAX_WAIT_WORK = MYSQL_WORK_PER_THREAD * 50;
/**
* 获取数据库存储任务的超时时间,单位毫秒
*/
public static final int MYSQLWORK_TIMEOUT = 200;
/**
* 默认request表名
*/
public static String REQUEST_TABLE;
/**
* 默认result表名
*/
public static String RESULT_TABLE;
/**
* 默认class表名
*/
public static String CLASS_TABLE;
/**
* 默认性能测试表名
*/
public static String PERFORMANCE_TABLE;
/**
* 默认的alertover表格
*/
public static String ALERTOVER_TABLE;
/**
* 是否保存所有的请求到数据库
*/
public static boolean flag = propertyUtils.getPropertyBoolean("flag");
}
================================================
FILE: src/main/groovy/com/funtester/config/SysInit.java
================================================
package com.funtester.config;
import com.funtester.frame.SourceCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 存放一些系统初始化的方法,可被外部调用
*/
public class SysInit extends SourceCode{
private static Logger logger = LogManager.getLogger(SysInit.class);
/**
* 是否是黑名单的host
* <p>先检验fv1314和本地local还有10.10.的地址,然后校验配置文件中的host name</p>
*
* @param name
* @return
*/
public static boolean isBlack(String name) {
return name.contains("10.10") || name.contains("local") || HttpClientConstant.BLACK_HOSTS.contains(name);
}
}
================================================
FILE: src/main/groovy/com/funtester/config/VerifyType.groovy
================================================
package com.funtester.config;
import com.funtester.base.exception.ParamException;
import org.apache.commons.lang3.StringUtils
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger;
/**
* 通用验证类型,包含,正则,JsonPath,handle四项
*/
enum VerifyType {
CONTAIN("contain"), REGEX("regex"), JSONPATH("jsonpath"), HANDLE("handle");
String vname;
VerifyType(String vname) {
this.vname = vname;
}
private static Logger logger = LogManager.getLogger(VerifyType.class);
/**
* 获取验证类型,不区分大小写
*
* @param name
* @return
*/
static VerifyType getRequestType(String name) {
logger.debug("验证校验方式方式:{}", name);
if (StringUtils.isEmpty(name)) ParamException.fail("参数不能为空!");
name = name.toLowerCase();
switch (name) {
case CONTAIN.getVname():
return CONTAIN;
case REGEX.getVname():
return REGEX;
case JSONPATH.getVname():
return JSONPATH;
case HANDLE.getVname():
return HANDLE;
default:
ParamException.fail(name + "参数错误!");
}
}
}
================================================
FILE: src/main/groovy/com/funtester/db/mongodb/MongoBase.java
================================================
package com.funtester.db.mongodb;
import com.funtester.frame.SourceCode;
/**
* mongo操作类的基础类
*/
@SuppressWarnings("all")
public class MongoBase extends SourceCode {
//
// /**
// * 获取服务地址list
// *
// * @param addresses
// * @return
// */
// public static List<ServerAddress> getServers(ServerAddress... addresses) {
// return Arrays.asList(addresses);
// }
//
// /**
// * 获取服务地址
// *
// * @param host
// * @param port
// * @return
// */
// public static ServerAddress getServerAdress(String host, int port) {
// return new ServerAddress(host, port);
// }
//
// /**
// * 获取认证list
// *
// * @param credentials
// * @return
// */
// public static List<MongoCredential> getCredentials(MongoCredential... credentials) {
// return Arrays.asList(credentials);
// }
//
// /**
// * 获取验证
// *
// * @param userName
// * @param database
// * @param password
// * @return
// */
// public static MongoCredential getMongoCredential(String userName, String database, String password) {
// return MongoCredential.createCredential(userName, database, password.toCharArray());
// }
//
// /**
// * 获取mongo客户端
// *
// * @param addresses
// * @param credentials
// * @return
// */
// public static MongoClient getMongoClient(List<ServerAddress> addresses, List<MongoCredential> credentials) {
// return new MongoClient(addresses, credentials);
// }
//
// /**
// * 连接mongo数据库
// *
// * @param mongoClient
// * @param databaseName
// * @return
// */
// public static MongoDatabase getMongoDatabase(MongoClient mongoClient, String databaseName) {
// return mongoClient.getDatabase(databaseName);
// }
//
// /**
// * 连接mongo集
// *
// * @param mongoDatabase
// * @param collectionName
// * @return
// */
// public static MongoCollection<Document> getMongoCollection(MongoDatabase mongoDatabase, String collectionName) {
// return mongoDatabase.getCollection(collectionName);
// }
//
// /**
// * 关闭数据库连接
// *
// * @param mongoClient
// */
// public static void over(MongoClient mongoClient) {
// mongoClient.close();
// }
//
// /**
// * 获取mongo客户端对象,通过servers和credentials对象创建
// *
// * @param mongoObject
// * @return
// */
// public static MongoClient getMongoClient(MongoObject mongoObject) {
// MongoClient mongoClient = new MongoClient(getServers(getServerAdress(mongoObject.host, mongoObject.port)), getCredentials(getMongoCredential(mongoObject.user, mongoObject.database, mongoObject.password)));
// return mongoClient;
// }
//
// /**
// * 获取mongo客户端对象,通过uri方式连接
// *
// * @param mongoObject
// * @return
// */
// public static MongoClient getMongoClientOnline(MongoObject mongoObject) {
// String format = String.format("mongodb://%s:%s@%s:%d/%s", mongoObject.user, mongoObject.password, mongoObject.host, mongoObject.port, mongoObject.database);
// return new MongoClient(new MongoClientURI(format));
// }
//
// /**
// * 获取collection对象
// *
// * @param mongoObject
// * @return
// */
// public static MongoCollection<Document> getCollection(MongoObject mongoObject, String collectionName) {
// return getMongoClient(mongoObject).getDatabase(mongoObject.database).getCollection(collectionName);
// }
//
// /**
// * 获取collection对象
// *
// * @param mongoObject
// * @return
// */
// public static MongoCollection<Document> getCollectionOnline(MongoObject mongoObject, String collectionName) {
// return getMongoClientOnline(mongoObject).getDatabase(mongoObject.database).getCollection(collectionName);
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mongodb/MongoObject.java
================================================
package com.funtester.db.mongodb;
/**
* mongo数据库配置对象,针对单个数据服务,单个身份验证
*/
public class MongoObject extends MongoBase {
//
// String host;
//
// int port;
//
// String user;
//
// String password;
//
// String database;
//
// MongoClient mongoClient;
//
// /**
// * 创建测试数据连接
// *
// * @param host
// * @param port
// * @param user
// * @param password
// * @param database
// */
// public MongoObject(String host, int port, String user, String password, String database) {
// this.host = host;
// this.port = port;
// this.user = user;
// this.password = password;
// this.database = database;
// this.mongoClient = getMongoClient(this);
// }
//
// /**
// * 创建线上数据库连接
// *
// * @param port
// * @param host
// * @param user
// * @param password
// * @param database
// */
// public MongoObject(int port, String host, String user, String password, String database) {
// this.host = host;
// this.port = port;
// this.user = user;
// this.password = password;
// this.database = database;
// this.mongoClient = getMongoClientOnline(this);
// }
//
// /**
// * 获取colletion对象
// *
// * @param collectionName
// * @return
// */
// public MongoCollection<Document> getMongoCollection(String collectionName) {
// MongoClient mongoClientOnline = getMongoClientOnline(this);
// return mongoClientOnline.getDatabase(database).getCollection(collectionName);
// }
//
//
// /**
// * 关闭连接
// */
// public void over() {
// over(this.mongoClient);
// }
//
// @Override
// public MongoObject clone() {
// return new MongoObject(this.host, this.port, this.user, this.password, this.database);
// }
//
// public MongoObject clone2() {
// return new MongoObject(this.port, this.host, this.user, this.password, this.database);
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/AidThread.java
================================================
package com.funtester.db.mysql;
import com.funtester.frame.SourceCode;
/**
* mysql辅助线程,当任务数太满的时候启用
* <p>已经启用,单独写了基于springboot的sql存储服务</p>
*/
@Deprecated
public class AidThread extends SourceCode {
//public class AidThread extends SourceCode implements Runnable {
// private static Logger logger = LogManager.getLogger(AidThread.class);
//
// @Override
// public void run() {
// MySqlObject object = new MySqlObject();
// MySqlObject.threadNum.incrementAndGet();
// while (true) {
// if (object.statement == null || MySqlTest.getWaitWorkNum() < SqlConstant.MYSQL_WORK_PER_THREAD) break;
// String sql = MySqlTest.getWork();
// if (sql == null) break;
// logger.info("辅助线程执行SQL:{}", sql);
// object.executeUpdateSql(sql);
// }
// MySqlObject.threadNum.decrementAndGet();
// object.close();
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/MySqlFun.java
================================================
package com.funtester.db.mysql;
/**
* mysql操作的基础类
* <p>用于存储数据,多用于爬虫</p>
*/
@Deprecated
public class MySqlFun extends SqlBase {
//public class MySqlFun extends SqlBase implements IMySqlBasic {
// String url;
//
// String database;
//
// String user;
//
// String password;
//
// Connection connection;
//
// Statement statement;
//
// /**
// * 私有构造方法
// */
// public MySqlFun(String url, String database, String user, String password) {
// this.url = url;
// this.database = database;
// this.user = user;
// this.password = password;
// getConnection(database);
// }
//
// /**
// * 初始化连接
// */
// @Override
// public void getConnection() {
// getConnection(EMPTY);
// }
//
// /**
// * 执行sql语句,非query语句,并不关闭连接
// *
// * @param sql
// */
// @Override
// public void executeUpdateSql(String sql) {
// executeUpdateSql(EMPTY, sql);
// }
//
// /**
// * 执行sql语句,非query语句,并不关闭连接
// *
// * @param database
// * @param sql
// */
// @Override
// public void executeUpdateSql(String database, String sql) {
// getConnection(database);
// SqlBase.executeUpdateSql(connection, statement, sql);
// }
//
// /**
// * 查询功能
// *
// * @param sql
// * @return
// */
// @Override
// public ResultSet executeQuerySql(String sql) {
// return SqlBase.executeQuerySql(connection, statement, sql);
// }
//
// @Override
// public ResultSet executeQuerySql(String database, String sql) {
// getConnection(database);
// return executeQuerySql(sql);
// }
//
// /**
// * 关闭query连接
// */
// @Override
// public void mySqlOver() {
// SqlBase.mySqlOver(connection, statement);
// }
//
// @Override
// public void getConnection(String database) {
// connection = SqlBase.getConnection(SqlConstant.FUN_SQL_URL.replace("ip", url).replace("database", database), user, password);
// statement = SqlBase.getStatement(connection);
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/MySqlObject.java
================================================
package com.funtester.db.mysql;
/**
* 辅助线程,处理sql任务
* <p>不再使用该方式存储数据库数据</p>
*/
@Deprecated
public class MySqlObject {
// /**
// * 标记多少辅助线程存活数量
// */
// public static AtomicInteger threadNum = new AtomicInteger(0);
// /**
// * 标记连接使用
// */
// int updateTime;
// Connection connection;
// Statement statement;
//
// /**
// * 初始化连接方法
// */
// public MySqlObject() {
// getConnection();
// }
//
// /**
// * 获取当前辅助线程数
// *
// * @return
// */
// public static int getThreadNum() {
// return threadNum.get();
// }
//
// /**
// * 更新连接使用标记
// */
// void updateLastUpdate() {
// updateTime = SourceCode.getMark();
// }
//
// /**
// * 执行sql方法
// *
// * @param sql
// */
// void executeUpdateSql(String sql) {
// getConnection();
// SqlBase.executeUpdateSql(connection, statement, sql);
// updateLastUpdate();
// }
//
// /**
// * 获取数据库连接
// */
// void getConnection() {
// try {
// if (SourceCode.getMark() - updateTime > SqlConstant.MYSQL_RECONNECTION_GAP || connection == null || connection.isClosed()) {
// connection = TestConnectionManage.getConnection(SqlConstant.TEST_SQL_URL, SqlConstant.TEST_USER, SqlConstant.TEST_PASS_WORD);
// statement = TestConnectionManage.getStatement(connection);
// }
// } catch (SQLException e) {
// Output.output("数据库连接获取失败!", e);
// } finally {
// updateLastUpdate();
// }
// }
//
// /**
// * 关闭对象方法
// */
// void close() {
// SqlBase.mySqlOver(connection, statement);
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/MySqlTest.java
================================================
package com.funtester.db.mysql;
import com.alibaba.fastjson.JSONObject;
import com.funtester.base.bean.PerformanceResultBean;
import com.funtester.base.bean.RecordBean;
import com.funtester.base.bean.RequestInfo;
import com.funtester.config.SqlConstant;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.Statement;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 数据库读写类
* <p>
* 用来存储接口请求信息的mysql数据库类
* 打印请求信息的方法写在这里面,数据库服务的队列也在这里(可不用),暂时才用直接抛出sql语句完成记录功能
* </p>
*/
public class MySqlTest extends SqlBase {
private static Logger logger = LogManager.getLogger(MySqlTest.class);
/**
* 控台statement1和statement均衡
*/
static AtomicInteger key = new AtomicInteger(0);
/**
* 存放数据库存储任务
*/
static LinkedBlockingQueue<String> sqls = new LinkedBlockingQueue<>();
public static Connection getConnection0() {
return connection0;
}
public static Statement getStatement0() {
return statement0;
}
/**
* 用于查询
*/
static Connection connection0;
/**
* 用于写入
*/
static Connection connection1;
/**
* 用于写入
*/
static Connection connection2;
static Statement statement0;
static Statement statement1;
static Statement statement2;
/**
* 新方法,报错requestinfo对象
*
* @param requestInfo 请求信息
* @param data_size
* @param expend_time
* @param status
* @param mark
* @param code
* @param localIP
* @param computerName
*/
public static void saveApiTestDate(RequestInfo requestInfo, int data_size, long expend_time, int status, int mark, int code, String localIP, String computerName) {
logger.info("请求uri:{},耗时:{} ms, {}", requestInfo.getUri(), expend_time, requestInfo.mark());
// if (StringUtils.isEmpty(SqlConstant.REQUEST_TABLE) || SysInit.isBlack(requestInfo.getHost())) return;
// String sql = String.format("INSERT INTO " + SqlConstant.REQUEST_TABLE + " (domain,api,data_size,expend_time,status,type,method,code,local_ip,local_name,create_time) VALUES ('%s','%s',%d,%d,%d,'%s','%s',%d,'%s','%s','%s');", requestInfo.getHost(), requestInfo.getApiName(), data_size, expend_time, status, requestInfo.getType(), requestInfo.getMethod().getName(), code, localIP, computerName, Time.getDate());
// RecordBean requestBean = new RecordBean();
// requestBean.setApi(requestInfo.getApiName());
// requestBean.setDomain(requestInfo.getHost());
// requestBean.setType(requestInfo.getType());
// requestBean.setExpend_time(expend_time);
// requestBean.setData_size(data_size);
// requestBean.setStatus(status);
// requestBean.setMethod(requestInfo.getMethod().getName());
// requestBean.setCode(code);
// requestBean.setLocal_ip(localIP);
// requestBean.setLocal_name(computerName);
// requestBean.setCreate_time(Time.getDate());
// RecordBean.get().setApi(requestInfo.getApiName()).setDomain(requestInfo.getHost()).setType(requestInfo.getType()).setExpend_time(expend_time).setData_size(data_size).setStatus(status).setMethod(requestInfo.getMethod().getName()).setCode(code).setLocal_ip(localIP).setLocal_name(computerName).setCreate_time(Time.getDate());
// sendWork(sql);
}
/**
* 保存性能测试结果的方法
*
* @param bean
*/
public static void savePerformanceBean(PerformanceResultBean bean) {
if (!StringUtils.isNoneEmpty(SqlConstant.PERFORMANCE_TABLE)) return;
String sql = String.format("INSERT INTO " + SqlConstant.PERFORMANCE_TABLE + "(threads,total,rt,qps,error,fail,des,start_time,end_time) VALUES (%d,%d,%d,%f,%f,%f,'%s','%s','%s');", bean.getThreads(), bean.getTotal(), bean.getRt(), bean.getQps(), bean.getErrorRate(), bean.getFailRate(), bean.getMark(), bean.getStartTime(), bean.getEndTime());
sendWork(sql);
}
/**
* 保存测试结果
*
* @param label 测试标记
* @param result 测试结果
*/
public static void saveTestResult(String label, JSONObject result) {
// if (SqlConstant.RESULT_TABLE == null) return;
// String data = result.toString();
// Iterator<String> iterator = result.keySet().iterator();
// int abc = 1;
// while (iterator.hasNext() && abc == 1) {
// String key = iterator.next().toString();
// String value = result.getString(key);
// if (value.equals("false")) abc = 2;
// }
// if (abc != 1) new AlertOver("用例失败!", label + "测试结果:" + abc + LINE + data).sendBusinessMessage();
// logger.info(label + LINE + "测试结果:" + (abc == 1 ? "通过" : "失败") + LINE + data);
// String sql = String.format("INSERT INTO " + SqlConstant.RESULT_TABLE + " (result,label,params,local_ip,computer_name,create_time) VALUES (%d,'%s','%s','%s','%s','%s')", abc, label, data, LOCAL_IP, COMPUTER_USER_NAME, Time.getDate());
// sendWork(sql);
}
/**
* 记录alertover警告
*
* @param requestInfo
* @param type
* @param title
* @param localIP
* @param computerName
*/
public static void saveAlertOverMessage(RequestInfo requestInfo, String type, String title, String localIP, String computerName) {
// String host_name = requestInfo.getHost();
// if (SysInit.isBlack(host_name) || SqlConstant.ALERTOVER_TABLE == null) return;
// String sql = String.format("INSERT INTO alertover (type,title,host_name,api_name,local_ip,computer_name,create_time) VALUES('%s','%s','%s','%s','%s','%s','%s');", type, title, host_name, requestInfo.getApiName(), localIP, computerName, Time.getDate());
// sendWork(sql);
}
/**
* 获取所有有效的用例类
*
* @return
*/
// public static List<String> getAllCaseName() {
// List<String> list = new ArrayList<>();
// if (SqlConstant.CLASS_TABLE == null) return list;
// String sql = "SELECT * FROM " + SqlConstant.CLASS_TABLE + " WHERE flag = 1 ORDER BY create_time DESC;";
// TestConnectionManage.getQueryConnection();
// ResultSet resultSet = executeQuerySql(connection0, statement0, sql);
// try {
// while (resultSet != null && resultSet.next()) {
// String className = resultSet.getString("class");
// list.add(className);
// }
// } catch (SQLException e) {
// logger.warn(sql, e);
// }
// return list;
// }
/**
* 获取用例状态
*
* @param name
* @return
*/
// public static boolean getCaseStatus(String name) {
// if (SqlConstant.CLASS_TABLE == null) return false;
// String sql = "SELECT flag FROM " + SqlConstant.CLASS_TABLE + " WHERE class = \"" + name + "\";";
// TestConnectionManage.getQueryConnection();
// ResultSet resultSet = executeQuerySql(connection0, statement0, sql);
// try {
// if (resultSet != null && resultSet.next()) {
// int flag = resultSet.getInt(1);
// return flag == 1 ? true : false;
// }
// } catch (SQLException e) {
// logger.warn(sql, e);
// }
// return false;
// }
/**
* 确保所有的储存任务都结束
*/
// private static void check() {
// while (sqls.size() != 0) {
// sleep(100);
// }
// TestConnectionManage.stopAllThread();
// }
/**
* 执行sql语句,非query语句,并不关闭连接
*
* @param sql
* @param key
*/
// static void executeUpdateSql(String sql, boolean key) {
// int size = getWaitWorkNum();
// if (size % 3 == 1 && size > MySqlObject.getThreadNum() * (SqlConstant.MYSQL_WORK_PER_THREAD + 1) && size < SqlConstant.MYSQL_MAX_WAIT_WORK)
// new Thread(new AidThread()).start();
// if (key) {
// TestConnectionManage.getUpdateConnection1();
// executeUpdateSql(connection1, statement1, sql);
// TestConnectionManage.updateLastUpdate1();
// } else {
// TestConnectionManage.getUpdateConnection2();
// executeUpdateSql(connection2, statement2, sql);
// TestConnectionManage.updateLastUpdate2();
// }
// }
/**
* 发送数据库任务,暂时用请求服务器接口
*
* @param sql
* @return
*/
public static void sendWork(String sql) {
// if (!SqlConstant.flag) return;
// logger.debug("记录SQL:{}", sql);
// FunLibrary.noHeader();
// JSONObject argss = new JSONObject();
// argss.put("sql", DecodeEncode.urlEncoderText(sql));
// FunLibrary.getHttpResponse(FunLibrary.getHttpPost(SqlConstant.MYSQL_SERVER_PATH, argss));
}
/**
* 添加请求记录
*
* @param requestBean
*/
public static void sendWork(RecordBean requestBean) {
// FunLibrary.noHeader();
// if (SqlConstant.flag)
// FunLibrary.getHttpResponse(FunLibrary.getHttpPost(SqlConstant.MYSQL_SERVER_PATH, requestBean.toJson()));
}
/**
* 添加存储任务,数据库存储服务用
*
* @param sql
* @return
*/
public static boolean addWork(String sql) {
// try {
// sqls.put(sql);
// } catch (InterruptedException e) {
// logger.warn("添加数据库存储任务失败!", e);
// return false;
// }
return true;
}
/**
* 从任务池里面获取任务
*
* @return
*/
static String getWork() {
// String sql = null;
// try {
// sql = sqls.poll(SqlConstant.MYSQLWORK_TIMEOUT, TimeUnit.MILLISECONDS);
// } catch (InterruptedException e) {
// logger.warn("获取存储任务失败!", e);
// } finally {
// return sql;
// }
return EMPTY;
}
/**
* 获取等待任务数
*
* @return
*/
public static int getWaitWorkNum() {
return sqls.size();
}
/**
* 提供外部查询功能
*
* @param sql
* @return
*/
// public static ResultSet executeQuerySql(String sql) {
// TestConnectionManage.getQueryConnection();
// return executeQuerySql(connection0, statement0, sql);
// }
/**
* 关闭数据库链接的方法,供外部使用
*/
// public static void mySqlOver() {
// mySqlQueryOver();
// }
/**
* 关闭update连接
*/
// static void mySqlUpdateOver() {
// check();
// mySqlOver(connection1, statement1);
// mySqlOver(connection2, statement2);
// }
/**
* 关闭query连接
*/
// public static void mySqlQueryOver() {
// mySqlOver(connection0, statement0);
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/SqlBase.java
================================================
package com.funtester.db.mysql;
import com.funtester.frame.SourceCode;
/**
* 数据库基础类,主要公共的获取连接和操作对象
*/
public class SqlBase extends SourceCode {
// private static Logger logger = LogManager.getLogger(SqlBase.class);
//
// /**
// * 获取数据库连接
// *
// * @param url 地址,包括端口
// * @param user 用户名
// * @param passowrd 密码
// * @return
// */
// public static Connection getConnection(String url, String user, String passowrd) {
// logger.debug("连接数据库url:{},user:{},password:{}", url, user, passowrd);
// try {
// Class.forName(SqlConstant.DRIVE);
// } catch (ClassNotFoundException e) {
// logger.warn("加载驱动程序失败!", e);
// }
// try {
// return DriverManager.getConnection(url, user, passowrd);
// } catch (SQLException e) {
// logger.warn("数据库连接失败!", e);
// }
// return null;
// }
//
// /**
// * 获取statement对象
// *
// * @param connection
// * @return
// */
// public static Statement getStatement(Connection connection) {
// try {
// return connection.createStatement();
// } catch (SQLException e) {
// logger.warn("获取数据库连接失败!", e);
// } catch (ExceptionInInitializerError e) {
// logger.warn("初始化失败!", e);
// }
// return null;
// }
//
// /**
// * 执行sql语句,查询语句,返回ResultSet,并不关闭连接
// *
// * @param connection
// * @param statement
// * @param sql
// * @return
// */
// public static ResultSet executeQuerySql(Connection connection, Statement statement, String sql) {
// logger.debug("执行的SQL:{}", sql);
// try {
// if (connection != null && !connection.isClosed()) {
// ResultSet resultSet = statement.executeQuery(sql);
// return resultSet;
// }
// } catch (SQLException e) {
// logger.warn(sql, e);
// }
// return null;
// }
//
// /**
// * 执行sql语句,非query语句,不关闭连接
// *
// * @param connection
// * @param statement
// * @param sql
// */
// public static void executeUpdateSql(Connection connection, Statement statement, String sql) {
// logger.debug("执行的SQL:{}", sql);
// try {
// if (!connection.isClosed()) statement.executeUpdate(sql);
// } catch (SQLException e) {
// logger.warn(sql, e);
// }
// }
//
// /**
// * 关闭数据库资源
// *
// * @param connection
// * @param statement
// */
// public static void mySqlOver(Connection connection, Statement statement) {
// try {
// if (connection == null || connection.isClosed()) return;
// statement.close();
// connection.close();
// } catch (SQLException e) {
// logger.warn("关闭数据库链接失败!", e);
// }
// }
}
================================================
FILE: src/main/groovy/com/funtester/db/mysql/TestConnectionManage.java
================================================
package com.funtester.db.mysql;
/**
* 测试数据存储数据库连接管理类
* <p>放弃使用该方式存储,换成springboot数据库服务</p>
*/
@Deprecated
public class TestConnectionManage extends SqlBase {
// static Logger logger = LogManager.getLogger(TestConnectionManage.class);
//
// public static ExecuteThread executeThread1 = new ExecuteThread(true);
//
//
// public static ExecuteThread executeThread2 = new ExecuteThread(false);
//
//
// /**
// * 记录query的最后调用时间时间
// */
// private static int lastQuery;
//
// /**
// * 记录update的最后调用时间
// */
// private static int lastUpdate1;
//
// /**
// * 记录update的最后调用时间
// */
// private static int lastUpdate2;
//
// public static void start() {
// getUpdateConnection1();
// getUpdateConnection2();
// executeThread1.start();
// executeThread2.start();
// }
//
// static void getQueryConnection() {
// try {
// if (getMark() - lastQuery > SqlConstant.MYSQL_RECONNECTION_GAP || MySqlTest.connection0 == null || MySqlTest.connection0.isClosed())
// MySqlTestInitQuery();
// } catch (SQLException e) {
// logger.warn("数据库连接获取失败!", e);
// }
// }
//
// public static void getUpdateConnection1() {
// try {
// if (getMark() - lastUpdate1 > SqlConstant.MYSQL_RECONNECTION_GAP || MySqlTest.connection1 == null || MySqlTest.connection1.isClosed())
// MySqlTestInitUpdate(true);
// } catch (SQLException e) {
// logger.warn("数据库连接获取失败!", e);
// }
// }
//
// public static void getUpdateConnection2() {
// try {
// if (getMark() - lastUpdate2 > SqlConstant.MYSQL_RECONNECTION_GAP || MySqlTest.connection2 == null || MySqlTest.connection2.isClosed())
// MySqlTestInitUpdate(false);
// } catch (SQLException e) {
// logger.warn("数据库连接获取失败!", e);
// }
// }
//
// static void updateLastQuery() {
// lastQuery = getMark();
// }
//
// static void updateLastUpdate1() {
// lastUpdate1 = getMark();
// }
//
// static void updateLastUpdate2() {
// lastUpdate2 = getMark();
// }
//
// /**
// * 连接初始化,last自动赋值
// */
// private static void MySqlTestInitQuery() {
// updateLastQuery();
// MySqlTest.mySqlQueryOver();
// MySqlTest.connection0 = getConnection(SqlConstant.TEST_SQL_URL, SqlConstant.TEST_USER, SqlCo
gitextract_d43vbshl/
├── build.gradle
├── document/
│ ├── 7788.markdown
│ ├── api.markdown
│ ├── article.markdown
│ ├── base.markdown
│ ├── directory.markdown
│ └── update.markdown
├── long/
│ ├── 1
│ ├── 30
│ ├── poster.markdown
│ └── sql/
│ ├── performance.sql
│ └── request.sql
├── readme.markdown
├── settings.gradle
└── src/
└── main/
├── groovy/
│ └── com/
│ └── funtester/
│ ├── base/
│ │ ├── bean/
│ │ │ ├── AbstractBean.groovy
│ │ │ ├── PerformanceResultBean.groovy
│ │ │ ├── RecordBean.groovy
│ │ │ ├── RequestInfo.groovy
│ │ │ ├── Result.groovy
│ │ │ └── VerifyBean.groovy
│ │ ├── constaint/
│ │ │ ├── CaseBase.java
│ │ │ ├── FixedQpsThread.java
│ │ │ ├── ThreadBase.java
│ │ │ ├── ThreadLimitTimeCount.java
│ │ │ └── ThreadLimitTimesCount.java
│ │ ├── exception/
│ │ │ ├── FailException.java
│ │ │ ├── LoginException.java
│ │ │ ├── ParamException.java
│ │ │ ├── RequestException.java
│ │ │ └── VerifyException.java
│ │ └── interfaces/
│ │ ├── IBase.java
│ │ ├── IMessage.java
│ │ ├── IMySqlBasic.java
│ │ ├── ISocketClient.java
│ │ ├── ISocketVerify.java
│ │ ├── MarkRequest.java
│ │ ├── MarkThread.java
│ │ └── ReturnCode.java
│ ├── config/
│ │ ├── Constant.java
│ │ ├── EmailConstant.java
│ │ ├── HttpClientConstant.java
│ │ ├── PropertyUtils.groovy
│ │ ├── RequestType.java
│ │ ├── SocketConstant.java
│ │ ├── SqlConstant.java
│ │ ├── SysInit.java
│ │ └── VerifyType.groovy
│ ├── db/
│ │ ├── mongodb/
│ │ │ ├── MongoBase.java
│ │ │ └── MongoObject.java
│ │ ├── mysql/
│ │ │ ├── AidThread.java
│ │ │ ├── MySqlFun.java
│ │ │ ├── MySqlObject.java
│ │ │ ├── MySqlTest.java
│ │ │ ├── SqlBase.java
│ │ │ └── TestConnectionManage.java
│ │ └── redis/
│ │ ├── RedisPool.java
│ │ └── RedisUtil.java
│ ├── dubbo/
│ │ ├── DubboBase.java
│ │ ├── DubboInvokeParams.groovy
│ │ ├── DubboParamBase.groovy
│ │ └── DubboUtil.java
│ ├── frame/
│ │ ├── JsonVerify.groovy
│ │ ├── Output.java
│ │ ├── ResponseVerify.java
│ │ ├── Save.java
│ │ ├── SourceCode.java
│ │ ├── execute/
│ │ │ ├── Concurrent.java
│ │ │ ├── ExecuteGroovy.java
│ │ │ ├── ExecuteSource.java
│ │ │ ├── FixedQpsConcurrent.java
│ │ │ ├── Progress.java
│ │ │ ├── StatisticsUtil.java
│ │ │ └── ThreadPoolUtil.groovy
│ │ └── thread/
│ │ ├── FixedQpsHeaderMark.groovy
│ │ ├── FixedQpsParamMark.java
│ │ ├── HeaderMark.java
│ │ ├── ParamMark.java
│ │ ├── QuerySqlThread.java
│ │ ├── RequestThreadTime.java
│ │ ├── RequestThreadTimes.java
│ │ ├── RequestTimeFixedQps.java
│ │ ├── RequestTimesFixedQps.java
│ │ └── UpdateSqlThread.java
│ ├── httpclient/
│ │ ├── ClientManage.java
│ │ ├── FunLibrary.java
│ │ ├── FunRequest.groovy
│ │ └── GCThread.java
│ ├── main/
│ │ ├── ExecuteMethod.java
│ │ └── PerformanceFromFile.groovy
│ ├── socket/
│ │ ├── ScoketIOFunClient.java
│ │ └── WebSocketFunClient.java
│ └── utils/
│ ├── ArgsUtil.java
│ ├── CMD.java
│ ├── CountUtil.groovy
│ ├── CurlUtil.groovy
│ ├── DecodeEncode.java
│ ├── FileUtil.groovy
│ ├── HeapDumper.java
│ ├── Join.java
│ ├── JsonUtil.groovy
│ ├── RWUtil.java
│ ├── Regex.java
│ ├── StringUtil.groovy
│ ├── Time.java
│ ├── TimeWatch.groovy
│ ├── WriteHtml.java
│ ├── message/
│ │ ├── AlertOver.java
│ │ └── EmailUtil.java
│ ├── request/
│ │ ├── Request.java
│ │ ├── RequestFile.java
│ │ └── Swagger.java
│ └── xml/
│ ├── Attr.groovy
│ ├── NodeInfo.groovy
│ ├── XMLUtil.groovy
│ └── XMLUtil2.groovy
└── resources/
├── http.properties
├── log4j2.xml
├── mysql.properties
└── redis.properties
SYMBOL INDEX (572 symbols across 78 files)
FILE: long/sql/performance.sql
type `performance` (line 24) | CREATE TABLE `performance` (
FILE: long/sql/request.sql
type `request` (line 24) | CREATE TABLE `request` (
FILE: src/main/groovy/com/funtester/base/constaint/CaseBase.java
class CaseBase (line 10) | public abstract class CaseBase extends SourceCode {
method saveResult (line 18) | public void saveResult(String label, JSONObject result) {
method before (line 27) | public abstract boolean before();
method after (line 34) | public abstract boolean after();
method init (line 41) | public abstract boolean init();
FILE: src/main/groovy/com/funtester/base/constaint/FixedQpsThread.java
class FixedQpsThread (line 10) | public abstract class FixedQpsThread<F> extends ThreadBase<F> {
method FixedQpsThread (line 23) | public FixedQpsThread(F f, int limit, int qps, MarkThread markThread, ...
method FixedQpsThread (line 32) | protected FixedQpsThread() {
method run (line 36) | @Override
method before (line 57) | @Override
FILE: src/main/groovy/com/funtester/base/constaint/ThreadBase.java
class ThreadBase (line 18) | public abstract class ThreadBase<F> extends SourceCode implements Runnab...
method ThreadBase (line 68) | protected ThreadBase() {
method before (line 85) | public void before() {
method doing (line 94) | protected abstract void doing() throws Exception;
method after (line 99) | protected void after() {
method setCountDownLatch (line 111) | public void setCountDownLatch(CountDownLatch countDownLatch) {
method clone (line 124) | @Override
method initBase (line 130) | public void initBase() {
method status (line 143) | public boolean status() {
method multiply (line 153) | public List<ThreadBase> multiply(int num) {
method stop (line 161) | public static void stop() {
method needAbort (line 170) | public static boolean needAbort() {
FILE: src/main/groovy/com/funtester/base/constaint/ThreadLimitTimeCount.java
class ThreadLimitTimeCount (line 22) | public abstract class ThreadLimitTimeCount<F> extends ThreadBase<F> {
method ThreadLimitTimeCount (line 35) | public ThreadLimitTimeCount(F f, int time, MarkThread markThread) {
method ThreadLimitTimeCount (line 41) | protected ThreadLimitTimeCount() {
method run (line 45) | @Override
method status (line 82) | public boolean status() {
method before (line 89) | @Override
method after (line 94) | @Override
FILE: src/main/groovy/com/funtester/base/constaint/ThreadLimitTimesCount.java
class ThreadLimitTimesCount (line 20) | public abstract class ThreadLimitTimesCount<F> extends ThreadBase<F> {
method ThreadLimitTimesCount (line 32) | public ThreadLimitTimesCount(F f, int times, MarkThread markThread) {
method ThreadLimitTimesCount (line 38) | protected ThreadLimitTimesCount() {
method run (line 42) | @Override
method before (line 79) | @Override
method status (line 84) | @Override
method after (line 90) | @Override
FILE: src/main/groovy/com/funtester/base/exception/FailException.java
class FailException (line 8) | public class FailException extends RuntimeException {
method FailException (line 12) | public FailException() {
method FailException (line 16) | protected FailException(String message) {
method fail (line 20) | public static void fail(String message) {
method fail (line 27) | public static void fail() {
method fail (line 36) | public static void fail(Exception e) {
FILE: src/main/groovy/com/funtester/base/exception/LoginException.java
class LoginException (line 8) | public class LoginException extends FailException {
method LoginException (line 12) | private LoginException() {
method LoginException (line 16) | private LoginException(String message) {
method fail (line 20) | public static void fail(String message) {
method fail (line 29) | public static void fail(JSONObject response) {
FILE: src/main/groovy/com/funtester/base/exception/ParamException.java
class ParamException (line 8) | public class ParamException extends FailException {
method ParamException (line 12) | private ParamException() {
method ParamException (line 16) | private ParamException(String message) {
method fail (line 20) | public static void fail(String message) {
method fail (line 24) | public static void fail(JSONObject message) {
FILE: src/main/groovy/com/funtester/base/exception/RequestException.java
class RequestException (line 8) | public class RequestException extends FailException {
method RequestException (line 12) | private RequestException() {
method RequestException (line 16) | private RequestException(HttpRequestBase request) {
method RequestException (line 20) | public RequestException(String message) {
method fail (line 24) | public static void fail(HttpRequestBase base) {
method fail (line 28) | public static void fail(String message) {
FILE: src/main/groovy/com/funtester/base/exception/VerifyException.java
class VerifyException (line 11) | public class VerifyException extends FailException {
method VerifyException (line 15) | private VerifyException() {
method VerifyException (line 19) | private VerifyException(HttpRequestBase request) {
method VerifyException (line 23) | private VerifyException(String message) {
method fail (line 28) | public static void fail(String message) {
method fail (line 33) | public static void fail(JSONObject message) {
method fail (line 38) | public static void fail(HttpRequestBase request) {
FILE: src/main/groovy/com/funtester/base/interfaces/IBase.java
type IBase (line 15) | public interface IBase {
method getGet (line 23) | HttpGet getGet(String url);
method getGet (line 32) | HttpGet getGet(String url, JSONObject arg);
method getPost (line 40) | HttpPost getPost(String url);
method getPost (line 49) | HttpPost getPost(String url, JSONObject params);
method getPost (line 59) | HttpPost getPost(String url, JSONObject params, File file);
method getResponse (line 67) | JSONObject getResponse(HttpRequestBase request);
method getGetResponse (line 75) | JSONObject getGetResponse(String url);
method getGetResponse (line 84) | JSONObject getGetResponse(String url, JSONObject args);
method getPostResponse (line 92) | JSONObject getPostResponse(String url);
method getPostResponse (line 101) | JSONObject getPostResponse(String url, JSONObject params);
method getPostResponse (line 111) | JSONObject getPostResponse(String url, JSONObject params, File file);
method isRight (line 122) | boolean isRight(JSONObject response);
method checkCode (line 135) | int checkCode(JSONObject response, RequestInfo requestInfo);
method login (line 140) | void login();
method setHeaders (line 145) | void setHeaders(HttpRequestBase request);
method handleResponseHeader (line 152) | void handleResponseHeader(JSONObject response);
method getParams (line 159) | JSONObject getParams();
method init (line 167) | void init(JSONObject info);
method recordRequest (line 173) | void recordRequest(HttpRequestBase base);
method getRequest (line 180) | HttpRequestBase getRequest();
method print (line 187) | public void print(JSONObject response);
method printHeader (line 195) | public void printHeader(HttpRequestBase request);
method printHeader (line 202) | public void printHeader(CloseableHttpResponse response);
FILE: src/main/groovy/com/funtester/base/interfaces/IMessage.java
type IMessage (line 3) | public interface IMessage {
method sendSystemMessage (line 7) | public void sendSystemMessage();
method sendFunctionMessage (line 12) | public void sendFunctionMessage();
method sendBusinessMessage (line 17) | public void sendBusinessMessage();
method sendCodeMessage (line 22) | public void sendCodeMessage();
method sendRemindMessage (line 27) | public void sendRemindMessage();
FILE: src/main/groovy/com/funtester/base/interfaces/IMySqlBasic.java
type IMySqlBasic (line 8) | public interface IMySqlBasic {
method executeQuerySql (line 15) | ResultSet executeQuerySql(String sql);
method executeQuerySql (line 24) | ResultSet executeQuerySql(String database, String sql);
method executeUpdateSql (line 31) | void executeUpdateSql(String sql);
method executeUpdateSql (line 39) | void executeUpdateSql(String database, String sql);
method mySqlOver (line 44) | void mySqlOver();
method getConnection (line 51) | void getConnection(String database);
method getConnection (line 56) | void getConnection();
FILE: src/main/groovy/com/funtester/base/interfaces/ISocketClient.java
type ISocketClient (line 14) | public interface ISocketClient {
method connect (line 19) | void connect();
method init (line 24) | void init();
method send (line 31) | void send(JSONObject mgs);
method send (line 38) | void send(String mgs);
method close (line 43) | void close();
method clone (line 48) | ISocketClient clone();
method isConnect (line 55) | boolean isConnect();
method getMsgs (line 62) | List<String> getMsgs();
method savaMsg (line 67) | void savaMsg();
FILE: src/main/groovy/com/funtester/base/interfaces/ISocketVerify.java
type ISocketVerify (line 10) | public interface ISocketVerify extends Runnable {
method initMsg (line 17) | public void initMsg(List<String> msg);
method verify (line 24) | public boolean verify();
method addVerify (line 31) | public void addVerify(VerifyBean bean);
method remoreVerify (line 38) | public void remoreVerify(VerifyBean bean);
method removeAllVerify (line 43) | public void removeAllVerify();
method saveResult (line 48) | public void saveResult();
FILE: src/main/groovy/com/funtester/base/interfaces/MarkRequest.java
type MarkRequest (line 8) | public interface MarkRequest extends MarkThread {
method mark (line 16) | public String mark(HttpRequestBase requestBase);
FILE: src/main/groovy/com/funtester/base/interfaces/MarkThread.java
type MarkThread (line 8) | public interface MarkThread {
method mark (line 16) | public String mark(ThreadBase threadBase);
method clone (line 18) | public MarkThread clone();
FILE: src/main/groovy/com/funtester/base/interfaces/ReturnCode.java
type ReturnCode (line 3) | public interface ReturnCode {
method getCode (line 5) | int getCode();
method getDesc (line 7) | String getDesc();
FILE: src/main/groovy/com/funtester/config/Constant.java
class Constant (line 19) | public class Constant {
method getSysInfo (line 80) | private static Properties getSysInfo() {
method getLocalIp (line 188) | public static String getLocalIp() {
method getLongFile (line 203) | public static String getLongFile(String fileName) {
method getPercent (line 207) | public static String getPercent(int i) {
method getPart (line 211) | public static String getPart(int i) {
FILE: src/main/groovy/com/funtester/config/EmailConstant.java
class EmailConstant (line 3) | public class EmailConstant {
FILE: src/main/groovy/com/funtester/config/HttpClientConstant.java
class HttpClientConstant (line 16) | public class HttpClientConstant {
method getProperty (line 20) | static String getProperty(String name) {
method addBlackHost (line 162) | public static void addBlackHost(String host) {
FILE: src/main/groovy/com/funtester/config/RequestType.java
type RequestType (line 10) | public enum RequestType {
method RequestType (line 18) | private RequestType(String name) {
method getRequestType (line 22) | public static RequestType getRequestType(String name) {
method getName (line 37) | public String getName() {
FILE: src/main/groovy/com/funtester/config/SocketConstant.java
class SocketConstant (line 7) | public class SocketConstant {
FILE: src/main/groovy/com/funtester/config/SqlConstant.java
class SqlConstant (line 7) | public class SqlConstant {
method getProperty (line 11) | static String getProperty(String name) {
FILE: src/main/groovy/com/funtester/config/SysInit.java
class SysInit (line 10) | public class SysInit extends SourceCode{
method isBlack (line 21) | public static boolean isBlack(String name) {
FILE: src/main/groovy/com/funtester/db/mongodb/MongoBase.java
class MongoBase (line 8) | @SuppressWarnings("all")
FILE: src/main/groovy/com/funtester/db/mongodb/MongoObject.java
class MongoObject (line 7) | public class MongoObject extends MongoBase {
FILE: src/main/groovy/com/funtester/db/mysql/AidThread.java
class AidThread (line 9) | @Deprecated
FILE: src/main/groovy/com/funtester/db/mysql/MySqlFun.java
class MySqlFun (line 7) | @Deprecated
FILE: src/main/groovy/com/funtester/db/mysql/MySqlObject.java
class MySqlObject (line 7) | @Deprecated
FILE: src/main/groovy/com/funtester/db/mysql/MySqlTest.java
class MySqlTest (line 24) | public class MySqlTest extends SqlBase {
method getConnection0 (line 38) | public static Connection getConnection0() {
method getStatement0 (line 42) | public static Statement getStatement0() {
method saveApiTestDate (line 80) | public static void saveApiTestDate(RequestInfo requestInfo, int data_s...
method savePerformanceBean (line 105) | public static void savePerformanceBean(PerformanceResultBean bean) {
method saveTestResult (line 117) | public static void saveTestResult(String label, JSONObject result) {
method saveAlertOverMessage (line 142) | public static void saveAlertOverMessage(RequestInfo requestInfo, Strin...
method sendWork (line 231) | public static void sendWork(String sql) {
method sendWork (line 245) | public static void sendWork(RecordBean requestBean) {
method addWork (line 257) | public static boolean addWork(String sql) {
method getWork (line 272) | static String getWork() {
method getWaitWorkNum (line 289) | public static int getWaitWorkNum() {
FILE: src/main/groovy/com/funtester/db/mysql/SqlBase.java
class SqlBase (line 8) | public class SqlBase extends SourceCode {
FILE: src/main/groovy/com/funtester/db/mysql/TestConnectionManage.java
class TestConnectionManage (line 7) | @Deprecated
FILE: src/main/groovy/com/funtester/db/redis/RedisPool.java
class RedisPool (line 8) | public class RedisPool extends SourceCode {
FILE: src/main/groovy/com/funtester/db/redis/RedisUtil.java
class RedisUtil (line 3) | public class RedisUtil extends RedisPool {
FILE: src/main/groovy/com/funtester/dubbo/DubboBase.java
class DubboBase (line 3) | public class DubboBase {
FILE: src/main/groovy/com/funtester/dubbo/DubboUtil.java
class DubboUtil (line 9) | public class DubboUtil extends SourceCode {
FILE: src/main/groovy/com/funtester/frame/Output.java
class Output (line 19) | @SuppressWarnings("all")
method output (line 28) | public static void output(AbstractBean bean) {
method output (line 37) | public static void output(String text) {
method output (line 49) | public static void output(Object... object) {
method output (line 70) | public static void output(List list) {
method output (line 74) | public static void output(Iterator its) {
method output (line 83) | public static void output(Iterable its) {
method output (line 87) | public static void output(Map map) {
method output (line 100) | public static void output(JSONArray jsonArray) {
method output (line 119) | public static <T extends Number> void output(T[] nums) {
method output (line 130) | public static <T extends Number> void output(T x) {
method output (line 134) | public static void output(Object o) {
method output (line 139) | public static void output(int[] nums) {
method output (line 143) | public static void output(long[] nums) {
method output (line 147) | public static void output(double[] nums) {
method output (line 156) | public static JSONObject output(JSONObject jsonObject) {
method show (line 205) | public static void show(Map map) {
method show (line 209) | public static void show(List<List<String>> rows) {
method showStr (line 218) | public static void showStr(String content) {
class ConsoleTable (line 227) | static class ConsoleTable extends SourceCode {
method show (line 231) | public static void show(Map map) {
method ConsoleTable (line 240) | private ConsoleTable(Map map) {
method ConsoleTable (line 261) | private ConsoleTable(List<List<String>> rows) {
method getCel (line 289) | public String getCel(int colum, String content) {
method getHeader (line 298) | private String getHeader() {
FILE: src/main/groovy/com/funtester/frame/ResponseVerify.java
class ResponseVerify (line 16) | public class ResponseVerify extends SourceCode {
method getLines (line 35) | public List<String> getLines() {
method ResponseVerify (line 44) | public ResponseVerify(JSONObject jsonObject) {
method getCode (line 55) | public int getCode() {
method isRight (line 65) | public boolean isRight() {
method getValue (line 75) | public String getValue(String key) {
method isContains (line 91) | public boolean isContains(String... text) {
method isNum (line 108) | public boolean isNum(String... value) {
method notNull (line 132) | public boolean notNull(String... keys) {
method isArray (line 155) | public boolean isArray(String key) {
method isJson (line 168) | public boolean isJson(String key) {
method isBoolean (line 182) | public boolean isBoolean(String... value) {
method isRegex (line 206) | public boolean isRegex(String regex) {
method parseJsonLines (line 217) | public static List<String> parseJsonLines(JSONObject response) {
FILE: src/main/groovy/com/funtester/frame/Save.java
class Save (line 20) | public class Save {
method info (line 29) | public static void info(String content) {
method info (line 33) | public static void info(String name, String content) {
method saveLongList (line 46) | public static void saveLongList(Collection<Long> data, Object name) {
method saveIntegerList (line 55) | public static void saveIntegerList(Collection<Integer> data, String na...
method saveDoubleList (line 64) | public static void saveDoubleList(Collection<Double> data, String name) {
method saveList (line 76) | public static void saveList(Collection<Object> data, String name) {
method saveStringList (line 85) | public static void saveStringList(Collection<String> data, String name) {
method saveJson (line 93) | public static void saveJson(JSONObject data, String name) {
method saveStringListSync (line 106) | public static void saveStringListSync(Collection<String> data, String ...
FILE: src/main/groovy/com/funtester/frame/SourceCode.java
class SourceCode (line 26) | public class SourceCode extends Output {
method getiMessage (line 34) | public static IMessage getiMessage() {
method setiMessage (line 38) | public static void setiMessage(IMessage iMessage) {
method initialValue (line 43) | @Override
method getMark (line 55) | public static int getMark() {
method getNanoMark (line 64) | public static long getNanoMark() {
method waitForKey (line 74) | public static void waitForKey(Object key) {
method getInput (line 94) | public static String getInput() {
method closeScanner (line 104) | public static void closeScanner() {
method changeArraysToJson (line 119) | public static JSONObject changeArraysToJson(Object[] objects, String r...
method getJson (line 137) | public static JSONObject getJson(String... content) {
method getSimpleJson (line 149) | public static JSONObject getSimpleJson(String key, Object value) {
method getManyString (line 162) | public static String getManyString(String text, int time) {
method getPercent (line 173) | public static double getPercent(int total, int piece) {
method getPercent (line 185) | public static String getPercent(double percent) {
method formatLong (line 195) | public static String formatLong(Number number) {
method formatDouble (line 205) | public static String formatDouble(Number number) {
method formatNumber (line 216) | public static String formatNumber(Number number, String pattern) {
method getRandomIP (line 226) | public static String getRandomIP() {
method changeStringToInt (line 236) | public static int changeStringToInt(String text) {
method changeStringToLong (line 252) | public static long changeStringToLong(String text) {
method changeStringToBoolean (line 268) | public static boolean changeStringToBoolean(String text) {
method changeStringToDouble (line 280) | public static double changeStringToDouble(String text) {
method isNumber (line 296) | public static boolean isNumber(String text) {
method isInteger (line 303) | public static boolean isInteger(String str) {
method isDouble (line 307) | public static boolean isDouble(String str) {
method sleep (line 316) | public static void sleep(int second) {
method sleep (line 330) | public static void sleep(double time) {
method sleep (line 344) | public static void sleep(long nanosec) {
method getRandomInt (line 359) | public static int getRandomInt(int num) {
method getRandomIntRange (line 370) | public static int getRandomIntRange(int start, int end) {
method random (line 382) | public static <F extends Number> F random(F... fs) {
method random (line 392) | public static String random(String... fs) {
method random (line 404) | public static <F extends Object> F random(List<F> list) {
method getRandomRange (line 416) | public static double getRandomRange(double start, double range) {
method getRandomDouble (line 425) | public static double getRandomDouble() {
method range (line 436) | public static IntStream range(int start, int end) {
method range (line 446) | public static IntStream range(int num) {
method fail (line 453) | public static void fail() {
method deepClone (line 467) | public static <T> T deepClone(T t) {
FILE: src/main/groovy/com/funtester/frame/execute/Concurrent.java
class Concurrent (line 25) | public class Concurrent extends SourceCode {
method Concurrent (line 94) | public Concurrent(ThreadBase thread, int threadNum, String desc) {
method Concurrent (line 103) | public Concurrent(List<ThreadBase> threads, String desc) {
method Concurrent (line 108) | private Concurrent(int threadNum, String desc) {
method Concurrent (line 115) | private Concurrent() {
method start (line 123) | public PerformanceResultBean start() {
method shutdownService (line 151) | private static void shutdownService(ExecutorService executorService, C...
method over (line 160) | private PerformanceResultBean over() {
method countQPS (line 176) | public PerformanceResultBean countQPS(int name, String desc, String st...
method countQPS (line 196) | public PerformanceResultBean countQPS(int name, String desc) {
FILE: src/main/groovy/com/funtester/frame/execute/ExecuteGroovy.java
class ExecuteGroovy (line 19) | public class ExecuteGroovy extends SourceCode {
method ExecuteGroovy (line 54) | public ExecuteGroovy(String path, String name) {
method executeAllMethod (line 63) | public void executeAllMethod(String path) {
method executeMethodByName (line 75) | public void executeMethodByName() {
method executeMethodByPath (line 89) | public void executeMethodByPath() {
method getGroovyObject (line 102) | public void getGroovyObject() {
method getAllGroovyFile (line 122) | public List<String> getAllGroovyFile(String path) {
FILE: src/main/groovy/com/funtester/frame/execute/ExecuteSource.java
class ExecuteSource (line 18) | public class ExecuteSource extends SourceCode {
method executeAllMethodInPackage (line 27) | public static void executeAllMethodInPackage(String packageName) {
method executeAllMethod (line 43) | public static void executeAllMethod(String path) {
method executeMethod (line 74) | public static void executeMethod(List<String> params) {
method executeMethod (line 85) | public static void executeMethod(String[] params) {
method executeMethod (line 94) | public static void executeMethod(String path, Object... paramsTpey) {
method getAllMethodName (line 146) | public static List<String> getAllMethodName(String path) {
method getClassName (line 170) | public static List<String> getClassName(String packageName) {
FILE: src/main/groovy/com/funtester/frame/execute/FixedQpsConcurrent.java
class FixedQpsConcurrent (line 27) | public class FixedQpsConcurrent extends SourceCode {
method FixedQpsConcurrent (line 83) | public FixedQpsConcurrent(FixedQpsThread thread, String desc) {
method FixedQpsConcurrent (line 94) | public FixedQpsConcurrent(List<FixedQpsThread> threads, String desc) {
method FixedQpsConcurrent (line 104) | private FixedQpsConcurrent(String desc) {
method FixedQpsConcurrent (line 109) | private FixedQpsConcurrent() {
method initPool (line 119) | public void initPool(int core, int max) {
method start (line 127) | public PerformanceResultBean start() {
method over (line 157) | private PerformanceResultBean over() {
method countQPS (line 175) | public PerformanceResultBean countQPS(int name, String desc, long star...
class AidThread (line 189) | class AidThread implements Runnable {
method AidThread (line 195) | public AidThread() {
method run (line 199) | @Override
method stop (line 224) | public void stop() {
FILE: src/main/groovy/com/funtester/frame/execute/Progress.java
class Progress (line 25) | public class Progress<F extends ThreadBase> extends SourceCode implement...
method Progress (line 105) | public Progress(final List<F> threads, String desc) {
method Progress (line 120) | public Progress(final List<F> threads, String desc, final AtomicIntege...
method init (line 131) | private void init() {
method run (line 150) | @Override
method getQPS (line 167) | private int getQPS() {
method stop (line 189) | public void stop() {
method printQPS (line 198) | private void printQPS() {
FILE: src/main/groovy/com/funtester/frame/execute/StatisticsUtil.java
class StatisticsUtil (line 53) | public class StatisticsUtil extends Constant {
method statistics (line 66) | public static String statistics(List<Integer> data, String title, int ...
method draw (line 82) | public static String draw(int[] data, String title) {
method draw (line 97) | public static String draw(List<Integer> data, String title) {
method batchNums (line 119) | public static List<Integer> batchNums(List<Integer> data) {
method getPercent (line 135) | private static String[] getPercent(int part, int total, int length) {
method getFileName (line 151) | public static String getFileName(int thread, String desc) {
method getFileName (line 161) | public static String getFileName(String desc) {
method getTrueName (line 171) | public static String getTrueName(String desc) {
FILE: src/main/groovy/com/funtester/frame/thread/FixedQpsParamMark.java
class FixedQpsParamMark (line 13) | public class FixedQpsParamMark extends SourceCode implements MarkThread,...
method mark (line 24) | @Override
method clone (line 29) | @Override
method FixedQpsParamMark (line 35) | private FixedQpsParamMark() {
method FixedQpsParamMark (line 39) | public FixedQpsParamMark(String name) {
FILE: src/main/groovy/com/funtester/frame/thread/HeaderMark.java
class HeaderMark (line 13) | public class HeaderMark extends SourceCode implements MarkRequest, Clone...
method mark (line 25) | @Override
method mark (line 45) | @Override
method clone (line 53) | @Override
method HeaderMark (line 58) | public HeaderMark(String headerName) {
method HeaderMark (line 63) | public HeaderMark() {
FILE: src/main/groovy/com/funtester/frame/thread/ParamMark.java
class ParamMark (line 13) | public class ParamMark extends SourceCode implements MarkThread, Cloneab...
method mark (line 26) | @Override
method clone (line 31) | @Override
method ParamMark (line 37) | public ParamMark() {
method ParamMark (line 41) | public ParamMark(String name) {
FILE: src/main/groovy/com/funtester/frame/thread/QuerySqlThread.java
class QuerySqlThread (line 14) | public class QuerySqlThread extends ThreadLimitTimesCount {
method QuerySqlThread (line 24) | public QuerySqlThread(IMySqlBasic base, String sql, int times) {
method before (line 30) | @Override
method doing (line 35) | @Override
method after (line 40) | @Override
method clone (line 46) | @Override
FILE: src/main/groovy/com/funtester/frame/thread/RequestThreadTime.java
class RequestThreadTime (line 15) | public class RequestThreadTime extends ThreadLimitTimeCount<HttpRequestB...
method RequestThreadTime (line 27) | public RequestThreadTime(HttpRequestBase request, int time) {
method RequestThreadTime (line 36) | public RequestThreadTime(HttpRequestBase request, int time, MarkThread...
method RequestThreadTime (line 40) | protected RequestThreadTime() {
method before (line 44) | @Override
method doing (line 50) | @Override
method clone (line 56) | @Override
FILE: src/main/groovy/com/funtester/frame/thread/RequestThreadTimes.java
class RequestThreadTimes (line 15) | public class RequestThreadTimes extends ThreadLimitTimesCount<HttpReques...
method RequestThreadTimes (line 27) | public RequestThreadTimes(HttpRequestBase request, int times) {
method RequestThreadTimes (line 38) | public RequestThreadTimes(HttpRequestBase request, int times, MarkThre...
method RequestThreadTimes (line 42) | protected RequestThreadTimes() {
method before (line 46) | @Override
method doing (line 55) | @Override
method clone (line 60) | @Override
FILE: src/main/groovy/com/funtester/frame/thread/RequestTimeFixedQps.java
class RequestTimeFixedQps (line 12) | public class RequestTimeFixedQps extends FixedQpsThread<HttpRequestBase> {
method RequestTimeFixedQps (line 18) | private RequestTimeFixedQps() {
method RequestTimeFixedQps (line 22) | public RequestTimeFixedQps(int qps, int time, MarkRequest markRequest,...
method before (line 26) | @Override
method doing (line 32) | @Override
method clone (line 37) | @Override
FILE: src/main/groovy/com/funtester/frame/thread/RequestTimesFixedQps.java
class RequestTimesFixedQps (line 12) | public class RequestTimesFixedQps extends FixedQpsThread<HttpRequestBase> {
method RequestTimesFixedQps (line 18) | private RequestTimesFixedQps() {
method RequestTimesFixedQps (line 22) | public RequestTimesFixedQps(int qps, int times, MarkRequest markReques...
method before (line 26) | @Override
method doing (line 32) | @Override
method clone (line 37) | @Override
FILE: src/main/groovy/com/funtester/frame/thread/UpdateSqlThread.java
class UpdateSqlThread (line 12) | public class UpdateSqlThread extends ThreadLimitTimesCount<String> {
method UpdateSqlThread (line 20) | public UpdateSqlThread(IMySqlBasic base, String sql, int times) {
method doing (line 26) | @Override
method after (line 31) | @Override
method clone (line 37) | @Override
FILE: src/main/groovy/com/funtester/httpclient/ClientManage.java
class ClientManage (line 55) | public class ClientManage {
method getPool (line 99) | private static PoolingHttpClientConnectionManager getPool() {
method getNPool (line 120) | private static PoolingNHttpClientConnectionManager getNPool() {
method createIgnoreVerifySSL (line 142) | private static SSLContext createIgnoreVerifySSL() {
method getHttpRequestRetryHandler (line 179) | private static HttpRequestRetryHandler getHttpRequestRetryHandler() {
method getCloseableHttpAsyncClient (line 232) | private static CloseableHttpAsyncClient getCloseableHttpAsyncClient() {
method getCloseableHttpsClients (line 236) | private static CloseableHttpClient getCloseableHttpsClients() {
method getRequestConfig (line 250) | private static RequestConfig getRequestConfig() {
method getProxyRequestConfig (line 261) | public static RequestConfig getProxyRequestConfig(String ip, int port) {
method recyclingConnection (line 269) | public static void recyclingConnection() {
method init (line 287) | public static void init(int timeout, int accepttime, int retrytimes, S...
FILE: src/main/groovy/com/funtester/httpclient/FunLibrary.java
class FunLibrary (line 47) | public class FunLibrary extends SourceCode {
method getHttpGet (line 74) | public static HttpGet getHttpGet(String url, JSONObject args) {
method getHttpGet (line 87) | public static HttpGet getHttpGet(String url) {
method getHttpPost (line 100) | public static HttpPost getHttpPost(String url, JSONObject params) {
method getHttpPost (line 114) | public static HttpPost getHttpPost(String url) {
method getHttpPost (line 127) | public static HttpPost getHttpPost(String url, String params) {
method getHttpPost (line 143) | public static HttpPost getHttpPost(String url, JSONObject args, String...
method getHttpPost (line 156) | public static HttpPost getHttpPost(String url, JSONObject args, JSONOb...
method getHttpPost (line 171) | public static HttpPost getHttpPost(String url, JSONObject args, JSONOb...
method getHttpPost (line 184) | public static HttpPost getHttpPost(String url, JSONObject params, File...
method setMultipartEntityEntity (line 197) | private static void setMultipartEntityEntity(HttpPost httpPost, JSONOb...
method beforeRequest (line 227) | protected static void beforeRequest(HttpRequestBase request) {
method afterResponse (line 237) | private static JSONObject afterResponse(CloseableHttpResponse response) {
method getJsonResponse (line 254) | private static JSONObject getJsonResponse(String content, JSONObject c...
method getContent (line 278) | public static String getContent(HttpResponse response) {
method getStatus (line 297) | public static int getStatus(CloseableHttpResponse response, JSONObject...
method getHttpResponse (line 313) | public static JSONObject getHttpResponse(HttpRequestBase request) {
method isRightRequest (line 349) | private static boolean isRightRequest(HttpRequestBase request) {
method setFormHttpEntity (line 361) | private static void setFormHttpEntity(HttpPost httpPost, JSONObject pa...
method parseResponeEntityByChar (line 375) | @Deprecated
method parseResponeByFile (line 397) | @Deprecated
method changeJsonToArguments (line 419) | public static String changeJsonToArguments(JSONObject argument) {
method getCookies (line 429) | public static Header getCookies(JSONObject cookies) {
method getHeader (line 440) | public static Header getHeader(String name, String value) {
method getiBase (line 444) | public static IBase getiBase() {
method setiBase (line 448) | public static void setiBase(IBase iBase) {
method header2Json (line 458) | public static JSONObject header2Json(List<Header> headers) {
method executeSimlple (line 469) | public static String executeSimlple(HttpRequestBase request) throws IO...
method setProxy (line 483) | public static void setProxy(HttpRequestBase request, String adress) {
method setProxy (line 494) | public static void setProxy(HttpRequestBase request, String ip, int po...
method getProxyConfig (line 504) | public static RequestConfig getProxyConfig(String adress) {
method executeSync (line 516) | public static void executeSync(HttpRequestBase request) {
method executeSyncWithResponse (line 529) | public static JSONObject executeSyncWithResponse(HttpRequestBase reque...
method getLastRequest (line 547) | public static HttpRequestBase getLastRequest() {
method testOver (line 554) | public static void testOver() {
method init (line 570) | public synchronized static void init(int timeout, int accepttime, int ...
method init (line 574) | public synchronized static void init(int timeout, int accepttime, int ...
FILE: src/main/groovy/com/funtester/httpclient/GCThread.java
class GCThread (line 10) | public class GCThread implements Runnable {
method starts (line 20) | public synchronized static void starts() {
method init (line 30) | public static synchronized Thread init() {
method GCThread (line 35) | private GCThread() {
method run (line 43) | @Override
method stop (line 54) | public static void stop() {
FILE: src/main/groovy/com/funtester/main/ExecuteMethod.java
class ExecuteMethod (line 7) | public class ExecuteMethod extends SourceCode {
method main (line 9) | public static void main(String[] args) {
FILE: src/main/groovy/com/funtester/socket/ScoketIOFunClient.java
class ScoketIOFunClient (line 6) | public class ScoketIOFunClient {
FILE: src/main/groovy/com/funtester/socket/WebSocketFunClient.java
class WebSocketFunClient (line 6) | public class WebSocketFunClient {
FILE: src/main/groovy/com/funtester/utils/ArgsUtil.java
class ArgsUtil (line 9) | public class ArgsUtil extends SourceCode {
method ArgsUtil (line 13) | public ArgsUtil(String[] args) {
method getIntOrdefault (line 24) | public int getIntOrdefault(int i, int k) {
method getBooleanOrdefault (line 35) | public boolean getBooleanOrdefault(int i, boolean k) {
method getStringOrdefault (line 45) | public String getStringOrdefault(int i, String k) {
method getFileOrDefault (line 55) | public File getFileOrDefault(int i, String path) {
method getJsonOrDefault (line 64) | public JSONObject getJsonOrDefault(int i, String json) {
FILE: src/main/groovy/com/funtester/utils/CMD.java
class CMD (line 14) | public class CMD extends Constant {
method execCmd (line 23) | public static int execCmd(String cmd) {
method execCmd (line 32) | public static int execCmd(String cmd, Charset charset) {
method execCmd (line 36) | public static int execCmd(String cmd, boolean filter, String mark) {
method execCmd (line 40) | public static int execCmd(String cmd, Charset charset, boolean filter,...
method catFile (line 78) | public static String catFile(String path, int num) {
FILE: src/main/groovy/com/funtester/utils/DecodeEncode.java
class DecodeEncode (line 24) | public class DecodeEncode extends Constant{
method urlEncoderText (line 34) | public static String urlEncoderText(String text) {
method urlEncoderText (line 44) | public static String urlEncoderText(String text, Charset charset) {
method urlDecoderText (line 60) | public static String urlDecoderText(String text, Charset charset) {
method urlDecoderText (line 76) | public static String urlDecoderText(String text) {
method base64Decode (line 86) | public static String base64Decode(String text) {
method base64Decode (line 97) | public static String base64Decode(String text, Charset charset) {
method base64Byte (line 107) | public static byte[] base64Byte(byte[] text) {
method base64Byte (line 117) | public static byte[] base64Byte(String text) {
method zipBase64 (line 127) | public static String zipBase64(String text) {
method unzipBase64 (line 145) | public static String unzipBase64(String text) {
method base64Encode (line 163) | public static String base64Encode(String text) {
method base64Encode (line 174) | public static String base64Encode(String text, Charset charset) {
method base64Encode (line 183) | public static String base64Encode(byte[] data) {
method encodeByMd5 (line 198) | public static String encodeByMd5(String text) {
method encodeByMd5 (line 232) | public static String encodeByMd5(String text, String salt) {
method unicodeToString (line 242) | public static String unicodeToString(String str) {
method unicodeToStringX (line 261) | public static String unicodeToStringX(String str) {
FILE: src/main/groovy/com/funtester/utils/HeapDumper.java
class HeapDumper (line 13) | public class HeapDumper {
method dumpHeap (line 33) | public static void dumpHeap(String fileName, boolean live) {
method initHotspotMBean (line 45) | private static void initHotspotMBean() {
FILE: src/main/groovy/com/funtester/utils/Join.java
class Join (line 6) | public class Join {
method join (line 15) | public static String join(String text, String separator) {
method join (line 26) | public static String join(String text, String separator, String prefix...
method join (line 39) | public static String join(Iterable<?> iterable, String separator, Stri...
method join (line 50) | public static String join(Iterable<?> iterable, String separator) {
method join (line 63) | public static String join(Object[] objects, String separator, String p...
method join (line 74) | public static String join(Object[] objects, String separator) {
FILE: src/main/groovy/com/funtester/utils/RWUtil.java
class RWUtil (line 22) | public class RWUtil extends Constant {
method readTxtByJson (line 32) | public static JSONObject readTxtByJson(String filePath) {
method readTxtByJson (line 45) | public static JSONObject readTxtByJson(String filePath, String filter) {
method readTextByString (line 64) | public static String readTextByString(String filePath) {
method readTxtFileByLine (line 80) | public static List<String> readTxtFileByLine(String filePath) {
method readTxtFileByLine (line 93) | public static List<String> readTxtFileByLine(String filePath, String c...
method readTxtFileByNumLine (line 128) | public static List<Integer> readTxtFileByNumLine(String filePath) {
method readTxtFileByDoubleLine (line 138) | public static List<Double> readTxtFileByDoubleLine(String filePath) {
method down (line 148) | public static void down(String url) {
method down (line 159) | public static void down(String url, String name) {
method copyFile (line 185) | public static void copyFile(String oldPath, String newPath) {
method writeText (line 216) | public static void writeText(File file, String text) {
FILE: src/main/groovy/com/funtester/utils/Regex.java
class Regex (line 17) | public class Regex extends SourceCode {
method isRegex (line 28) | public static boolean isRegex(String text, String regex) {
method isMatch (line 39) | public static boolean isMatch(String text, String regex) {
method matcher (line 50) | private static Matcher matcher(String text, String regex) {
method regexAll (line 62) | public static List<String> regexAll(String text, String regex) {
method findFirst (line 78) | public static String findFirst(String text, String regex) {
method getRegex (line 92) | @Deprecated
FILE: src/main/groovy/com/funtester/utils/Time.java
class Time (line 16) | public class Time extends SourceCode {
method initialValue (line 24) | @Override
method initialValue (line 34) | @Override
method initialValue (line 44) | @Override
method initialValue (line 56) | @Override
method getTimeStamp (line 69) | public static long getTimeStamp() {
method getStartOfDay (line 78) | public static String getStartOfDay() {
method getEndOfDay (line 87) | public static String getEndOfDay() {
method getUtcDate (line 96) | public static String getUtcDate() {
method getUtcTimestamp (line 108) | public static long getUtcTimestamp(String time) {
method getUtcTimestamp (line 120) | public static long getUtcTimestamp(long time) {
method getWeeksNum (line 129) | public static int getWeeksNum() {
method getMonth (line 138) | public static int getMonth() {
method getDay (line 147) | public static int getDay() {
method getYear (line 156) | public static int getYear() {
method getHour (line 160) | public static int getHour() {
method getMinute (line 164) | public static int getMinute() {
method getSecond (line 168) | public static int getSecond() {
method getNow (line 177) | public static String getNow() {
method markDate (line 181) | public static String markDate() {
method getNow (line 185) | public static String getNow(String format) {
method getNow (line 189) | public static String getNow(SimpleDateFormat now) {
method getTimeStamp (line 200) | public static long getTimeStamp(String time) {
method getTimeByTimestamp (line 216) | public static String getTimeByTimestamp(long time) {
method getTimeDiffer (line 229) | @Deprecated
method getTimeDiffer (line 241) | public static double getTimeDiffer(long start, long end) {
method getDate (line 250) | public static String getDate() {
FILE: src/main/groovy/com/funtester/utils/WriteHtml.java
class WriteHtml (line 8) | public class WriteHtml {
method getTable (line 16) | private static String getTable(List<Object> list) {
method createWebReport (line 33) | public static String createWebReport(List<List<Object>> result, String...
method getTr (line 54) | private static StringBuffer getTr(List<Object> tr) {
FILE: src/main/groovy/com/funtester/utils/message/AlertOver.java
class AlertOver (line 9) | public class AlertOver extends FunLibrary implements IMessage {
method AlertOver (line 33) | public AlertOver() {
method AlertOver (line 37) | public AlertOver(String title, String content) {
method AlertOver (line 42) | public AlertOver(String title, String content, String url) {
method AlertOver (line 47) | public AlertOver(String title, String content, String url, RequestInfo...
method sendSystemMessage (line 56) | public void sendSystemMessage() {
method sendFunctionMessage (line 66) | public void sendFunctionMessage() {
method sendBusinessMessage (line 73) | public void sendBusinessMessage() {
method sendCodeMessage (line 80) | public void sendCodeMessage() {
method sendRemindMessage (line 87) | public void sendRemindMessage() {
method sendMessage (line 96) | public void sendMessage(String source) {
FILE: src/main/groovy/com/funtester/utils/message/EmailUtil.java
class EmailUtil (line 8) | public class EmailUtil extends SourceCode {
FILE: src/main/groovy/com/funtester/utils/request/Request.java
class Request (line 15) | public class Request extends SourceCode {
method getUrl (line 22) | public String getUrl() {
method getType (line 26) | public RequestType getType() {
method getApiName (line 30) | public String getApiName() {
method getDesc (line 34) | public String getDesc() {
method getArgs (line 38) | public JSONObject getArgs() {
method getParams (line 42) | public JSONObject getParams() {
method getStringBuffer (line 46) | public StringBuffer getStringBuffer() {
method setUrl (line 55) | public void setUrl(String url) {
method setType (line 59) | public void setType(RequestType type) {
method setApiName (line 63) | public void setApiName(String apiName) {
method setDesc (line 67) | public void setDesc(String desc) {
method setArgs (line 71) | public void setArgs(JSONObject args) {
method setParams (line 75) | public void setParams(JSONObject params) {
method setStringBuffer (line 79) | public void setStringBuffer(StringBuffer stringBuffer) {
method setCode (line 83) | public void setCode(StringBuffer code) {
method spliceArgs (line 133) | private void spliceArgs(int i) {
method collectArgs (line 150) | private void collectArgs(String key, String value) {
method collectRestfulArgs (line 158) | private void collectRestfulArgs() {
method spliceUrl (line 173) | private String spliceUrl() {
method spliceGet (line 183) | private void spliceGet() {
method splicePost (line 196) | private void splicePost() {
method spliceEnd (line 220) | private String spliceEnd() {
method magic (line 233) | public String magic() {
FILE: src/main/groovy/com/funtester/utils/request/RequestFile.java
class RequestFile (line 17) | public class RequestFile extends FunLibrary {
method RequestFile (line 39) | public RequestFile(String name) {
method RequestFile (line 53) | public RequestFile(int i) {
method getInfo (line 60) | private void getInfo() {
method getParams (line 69) | private void getParams() {
method getRequest (line 79) | public HttpRequestBase getRequest() {
FILE: src/main/groovy/com/funtester/utils/request/Swagger.java
class Swagger (line 16) | public class Swagger extends FunLibrary {
method getRequest (line 58) | public Request getRequest() {
method getAllRequests (line 62) | public List<Request> getAllRequests() {
method Swagger (line 77) | public Swagger(String swaggerPath, String name) {
method Swagger (line 90) | public Swagger(String swaggerPath, String name, String url) {
method getKey (line 98) | public String getKey() {
method getRequests (line 108) | private void getRequests() {
method build (line 118) | public void build() {
method getRequest (line 134) | private Request getRequest(String url) {
method getUrls (line 182) | private void getUrls() {
method getNames (line 191) | private void getNames() {
method getPaths (line 202) | private void getPaths() {
Condensed preview — 119 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (528K chars).
[
{
"path": "build.gradle",
"chars": 5452,
"preview": "buildscript {\n\n repositories {\n maven {url 'http://maven.aliyun.com/nexus/content/groups/public/'}\n// m"
},
{
"path": "document/7788.markdown",
"chars": 16719,
"preview": "# 7788篇\n\n> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq."
},
{
"path": "document/api.markdown",
"chars": 10209,
"preview": "# 接口篇\n\n\n##### **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin."
},
{
"path": "document/article.markdown",
"chars": 45089,
"preview": "# 总目录\n\n> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.co"
},
{
"path": "document/base.markdown",
"chars": 8403,
"preview": "# 基础篇\n\n> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.co"
},
{
"path": "document/directory.markdown",
"chars": 151,
"preview": "* 由于文章链接存在敏感词问题,请各位看官移步\n \n## [GitHub地址](https://github.com/JunManYuanLong/FunTester/blob/okay/document/article.markdown"
},
{
"path": "document/update.markdown",
"chars": 8913,
"preview": "# 升级篇\n\n> **FunTester**,[腾讯云年度作者](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw)、[Boss直聘签约作者](https://mp.weixin.qq.co"
},
{
"path": "long/1",
"chars": 70,
"preview": "id=324,23,4,234,2,4,32,4,23\nsize1=9\nsize2=5\nc=5,10,15\na=3,6,9\nb=4,8,12"
},
{
"path": "long/30",
"chars": 11693,
"preview": "309\n163\n240\n173\n169\n114\n158\n87\n106\n107\n143\n131\n175\n112\n236\n230\n357\n83\n255\n191\n314\n120\n316\n81\n195\n107\n141\n163\n151\n123\n171"
},
{
"path": "long/poster.markdown",
"chars": 563,
"preview": "# FunTester广告位分享\n\n由于公众号资源有限,所以暂定每周一篇头条软文投放,为了方便特写此文档.\n\n日期周一代表当周.\n\nps:有兴趣可以一起聊一聊文末和次条.\n\n转载事宜直接微信联系\n\n[原创汇总,每周更新](https://g"
},
{
"path": "long/sql/performance.sql",
"chars": 1222,
"preview": "/*\n Navicat Premium Data Transfer\n\n Source Server : test\n Source Server Type : MySQL\n Source Server Version :"
},
{
"path": "long/sql/request.sql",
"chars": 1098,
"preview": "/*\n Navicat Premium Data Transfer\n\n Source Server : test\n Source Server Type : MySQL\n Source Server Version :"
},
{
"path": "readme.markdown",
"chars": 702,
"preview": "# 分支简介\n\n该分支为主分支,其他分支停止更新.\n\n> **FunTester**,[腾讯云年度作者,优秀讲师 | 腾讯云+社区权威认证](https://mp.weixin.qq.com/s/oeTeJZs6h4jJJMRyUunurw"
},
{
"path": "settings.gradle",
"chars": 30,
"preview": "rootProject.name = 'funtester'"
},
{
"path": "src/main/groovy/com/funtester/base/bean/AbstractBean.groovy",
"chars": 1505,
"preview": "package com.funtester.base.bean\n\nimport com.alibaba.fastjson.JSON\nimport com.alibaba.fastjson.JSONObject\nimport com.funt"
},
{
"path": "src/main/groovy/com/funtester/base/bean/PerformanceResultBean.groovy",
"chars": 1806,
"preview": "package com.funtester.base.bean\n\nimport com.funtester.db.mysql.MySqlTest\nimport com.funtester.frame.Output\nimport com.fu"
},
{
"path": "src/main/groovy/com/funtester/base/bean/RecordBean.groovy",
"chars": 1753,
"preview": "package com.funtester.base.bean\n\nimport org.apache.logging.log4j.LogManager\nimport org.apache.logging.log4j.Logger\n\n/**\n"
},
{
"path": "src/main/groovy/com/funtester/base/bean/RequestInfo.groovy",
"chars": 3657,
"preview": "package com.funtester.base.bean\n\nimport com.alibaba.fastjson.JSONObject\nimport com.funtester.base.interfaces.MarkRequest"
},
{
"path": "src/main/groovy/com/funtester/base/bean/Result.groovy",
"chars": 1500,
"preview": "package com.funtester.base.bean\n\nimport com.alibaba.fastjson.JSONObject\nimport com.funtester.base.interfaces.ReturnCode\n"
},
{
"path": "src/main/groovy/com/funtester/base/bean/VerifyBean.groovy",
"chars": 2832,
"preview": "package com.funtester.base.bean\n\nimport com.alibaba.fastjson.JSON\nimport com.funtester.base.exception.ParamException\nimp"
},
{
"path": "src/main/groovy/com/funtester/base/constaint/CaseBase.java",
"chars": 713,
"preview": "package com.funtester.base.constaint;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.db.mysql.MySqlTest;\n"
},
{
"path": "src/main/groovy/com/funtester/base/constaint/FixedQpsThread.java",
"chars": 1720,
"preview": "package com.funtester.base.constaint;\n\nimport com.funtester.base.interfaces.MarkThread;\nimport com.funtester.config.Http"
},
{
"path": "src/main/groovy/com/funtester/base/constaint/ThreadBase.java",
"chars": 3390,
"preview": "package com.funtester.base.constaint;\n\nimport com.funtester.base.interfaces.MarkThread;\nimport com.funtester.frame.Sourc"
},
{
"path": "src/main/groovy/com/funtester/base/constaint/ThreadLimitTimeCount.java",
"chars": 2733,
"preview": "package com.funtester.base.constaint;\n\nimport com.funtester.base.interfaces.MarkThread;\nimport com.funtester.config.Http"
},
{
"path": "src/main/groovy/com/funtester/base/constaint/ThreadLimitTimesCount.java",
"chars": 2630,
"preview": "package com.funtester.base.constaint;\n\nimport com.funtester.base.interfaces.MarkThread;\nimport com.funtester.config.Http"
},
{
"path": "src/main/groovy/com/funtester/base/exception/FailException.java",
"chars": 743,
"preview": "package com.funtester.base.exception;\n\nimport com.funtester.config.Constant;\n\n/**\n * 自定义异常基类\n */\npublic class FailExcept"
},
{
"path": "src/main/groovy/com/funtester/base/exception/LoginException.java",
"chars": 635,
"preview": "package com.funtester.base.exception;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 处理项目中的登录异常\n */\npublic class Login"
},
{
"path": "src/main/groovy/com/funtester/base/exception/ParamException.java",
"chars": 572,
"preview": "package com.funtester.base.exception;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 参数错误运行异常类\n */\npublic class ParamE"
},
{
"path": "src/main/groovy/com/funtester/base/exception/RequestException.java",
"chars": 676,
"preview": "package com.funtester.base.exception;\n\nimport org.apache.http.client.methods.HttpRequestBase;\n\n/**\n * 用于处理请求异常\n */\npubli"
},
{
"path": "src/main/groovy/com/funtester/base/exception/VerifyException.java",
"chars": 1086,
"preview": "package com.funtester.base.exception;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.frame.SourceCode;\nim"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/IBase.java",
"chars": 3536,
"preview": "package com.funtester.base.interfaces;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.base.bean.RequestIn"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/IMessage.java",
"chars": 409,
"preview": "package com.funtester.base.interfaces;\n\npublic interface IMessage {\n /**\n * 发送系统异常\n */\n public void sendSy"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/IMySqlBasic.java",
"chars": 840,
"preview": "package com.funtester.base.interfaces;\n\nimport java.sql.ResultSet;\n\n/**\n * 项目数据库执行类接口\n */\npublic interface IMySqlBasic {"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/ISocketClient.java",
"chars": 1027,
"preview": "package com.funtester.base.interfaces;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.socket.ScoketIOFunC"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/ISocketVerify.java",
"chars": 809,
"preview": "package com.funtester.base.interfaces;\n\nimport com.funtester.base.bean.VerifyBean;\n\nimport java.util.List;\n\n/**\n * Socke"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/MarkRequest.java",
"chars": 309,
"preview": "package com.funtester.base.interfaces;\n\nimport org.apache.http.client.methods.HttpRequestBase;\n\n/**\n * 专门用来标记HTTP请求的接口\n "
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/MarkThread.java",
"chars": 312,
"preview": "package com.funtester.base.interfaces;\n\nimport com.funtester.base.constaint.ThreadBase;\n\n/**\n * 用来标记thread,为了记录超时的请求\n */"
},
{
"path": "src/main/groovy/com/funtester/base/interfaces/ReturnCode.java",
"chars": 117,
"preview": "package com.funtester.base.interfaces;\n\npublic interface ReturnCode {\n\n int getCode();\n\n String getDesc();\n\n\n}\n"
},
{
"path": "src/main/groovy/com/funtester/config/Constant.java",
"chars": 5743,
"preview": "package com.funtester.config;\n\nimport com.funtester.utils.FileUtil;\nimport com.funtester.utils.Time;\nimport org.apache.h"
},
{
"path": "src/main/groovy/com/funtester/config/EmailConstant.java",
"chars": 457,
"preview": "package com.funtester.config;\n\npublic class EmailConstant {\n\n /**\n * QQ邮箱发件服务器\n */\n public static final St"
},
{
"path": "src/main/groovy/com/funtester/config/HttpClientConstant.java",
"chars": 3909,
"preview": "package com.funtester.config;\n\n\nimport org.apache.http.Header;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimp"
},
{
"path": "src/main/groovy/com/funtester/config/PropertyUtils.groovy",
"chars": 3954,
"preview": "package com.funtester.config\n\nimport com.alibaba.fastjson.JSONObject\nimport com.funtester.utils.RWUtil\nimport com.funtes"
},
{
"path": "src/main/groovy/com/funtester/config/RequestType.java",
"chars": 815,
"preview": "package com.funtester.config;\n\n\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.Logger;\n\n/**"
},
{
"path": "src/main/groovy/com/funtester/config/SocketConstant.java",
"chars": 672,
"preview": "package com.funtester.config;\n\n\n/**\n * socket测试相关的配置\n */\npublic class SocketConstant {\n\n /* WebSocket独享配置 */\n\n "
},
{
"path": "src/main/groovy/com/funtester/config/SqlConstant.java",
"chars": 1901,
"preview": "package com.funtester.config;\n\n\n/**\n *\n */\npublic class SqlConstant {\n\n static PropertyUtils.Property propertyUtils ="
},
{
"path": "src/main/groovy/com/funtester/config/SysInit.java",
"chars": 611,
"preview": "package com.funtester.config;\n\nimport com.funtester.frame.SourceCode;\nimport org.apache.logging.log4j.LogManager;\nimport"
},
{
"path": "src/main/groovy/com/funtester/config/VerifyType.groovy",
"chars": 1182,
"preview": "package com.funtester.config;\n\nimport com.funtester.base.exception.ParamException;\nimport org.apache.commons.lang3.Strin"
},
{
"path": "src/main/groovy/com/funtester/db/mongodb/MongoBase.java",
"chars": 3856,
"preview": "package com.funtester.db.mongodb;\n\nimport com.funtester.frame.SourceCode;\n\n/**\n * mongo操作类的基础类\n */\n@SuppressWarnings(\"al"
},
{
"path": "src/main/groovy/com/funtester/db/mongodb/MongoObject.java",
"chars": 1994,
"preview": "package com.funtester.db.mongodb;\n\n\n/**\n * mongo数据库配置对象,针对单个数据服务,单个身份验证\n */\npublic class MongoObject extends MongoBase {"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/AidThread.java",
"chars": 911,
"preview": "package com.funtester.db.mysql;\n\nimport com.funtester.frame.SourceCode;\n\n/**\n * mysql辅助线程,当任务数太满的时候启用\n * <p>已经启用,单独写了基于s"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/MySqlFun.java",
"chars": 2093,
"preview": "package com.funtester.db.mysql;\n\n/**\n * mysql操作的基础类\n * <p>用于存储数据,多用于爬虫</p>\n */\n@Deprecated\npublic class MySqlFun extends"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/MySqlObject.java",
"chars": 1707,
"preview": "package com.funtester.db.mysql;\n\n/**\n * 辅助线程,处理sql任务\n * <p>不再使用该方式存储数据库数据</p>\n */\n@Deprecated\npublic class MySqlObject {"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/MySqlTest.java",
"chars": 10673,
"preview": "package com.funtester.db.mysql;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.base.bean.PerformanceResul"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/SqlBase.java",
"chars": 2901,
"preview": "package com.funtester.db.mysql;\n\nimport com.funtester.frame.SourceCode;\n\n/**\n * 数据库基础类,主要公共的获取连接和操作对象\n */\npublic class S"
},
{
"path": "src/main/groovy/com/funtester/db/mysql/TestConnectionManage.java",
"chars": 3986,
"preview": "package com.funtester.db.mysql;\n\n/**\n * 测试数据存储数据库连接管理类\n * <p>放弃使用该方式存储,换成springboot数据库服务</p>\n */\n@Deprecated\npublic clas"
},
{
"path": "src/main/groovy/com/funtester/db/redis/RedisPool.java",
"chars": 2665,
"preview": "package com.funtester.db.redis;\n\nimport com.funtester.frame.SourceCode;\n\n/**\n * redis连接池\n */\npublic class RedisPool exte"
},
{
"path": "src/main/groovy/com/funtester/db/redis/RedisUtil.java",
"chars": 4449,
"preview": "package com.funtester.db.redis;\n\npublic class RedisUtil extends RedisPool {\n\n// public static int getIndex() {\n// "
},
{
"path": "src/main/groovy/com/funtester/dubbo/DubboBase.java",
"chars": 2335,
"preview": "package com.funtester.dubbo;\n\npublic class DubboBase {\n\n// private ApplicationConfig applicationConfig = new Applicat"
},
{
"path": "src/main/groovy/com/funtester/dubbo/DubboInvokeParams.groovy",
"chars": 251,
"preview": "package com.funtester.dubbo\n\n\nclass DubboInvokeParams {\n//\n// int length\n//\n// String[] types = new String[length]"
},
{
"path": "src/main/groovy/com/funtester/dubbo/DubboParamBase.groovy",
"chars": 228,
"preview": "package com.funtester.dubbo\n\nclass DubboParamBase {\n//\n// String type\n//\n// Object value\n//\n// DubboParamBase(C"
},
{
"path": "src/main/groovy/com/funtester/dubbo/DubboUtil.java",
"chars": 1670,
"preview": "package com.funtester.dubbo;\n\nimport com.funtester.frame.SourceCode;\n\n\n/**\n * dubbo泛化调用的方法\n */\npublic class DubboUtil ex"
},
{
"path": "src/main/groovy/com/funtester/frame/JsonVerify.groovy",
"chars": 8575,
"preview": "package com.funtester.frame\n\nimport com.alibaba.fastjson.JSON\nimport com.alibaba.fastjson.JSONArray\nimport com.alibaba.f"
},
{
"path": "src/main/groovy/com/funtester/frame/Output.java",
"chars": 9749,
"preview": "package com.funtester.frame;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibab"
},
{
"path": "src/main/groovy/com/funtester/frame/ResponseVerify.java",
"chars": 5887,
"preview": "package com.funtester.frame;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.httpclient.FunLibrary;\nimport"
},
{
"path": "src/main/groovy/com/funtester/frame/Save.java",
"chars": 3058,
"preview": "package com.funtester.frame;\n\nimport com.funtester.base.exception.FailException;\nimport com.funtester.utils.RWUtil;\nimpo"
},
{
"path": "src/main/groovy/com/funtester/frame/SourceCode.java",
"chars": 11660,
"preview": "package com.funtester.frame;\n\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.base.exception.FailException"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/Concurrent.java",
"chars": 5582,
"preview": "package com.funtester.frame.execute;\n\nimport com.funtester.base.bean.PerformanceResultBean;\nimport com.funtester.base.co"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/ExecuteGroovy.java",
"chars": 3261,
"preview": "package com.funtester.frame.execute;\n\nimport com.funtester.frame.SourceCode;\nimport com.funtester.utils.FileUtil;\nimport"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/ExecuteSource.java",
"chars": 6168,
"preview": "package com.funtester.frame.execute;\n\nimport com.alibaba.fastjson.JSON;\nimport com.funtester.base.exception.FailExceptio"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/FixedQpsConcurrent.java",
"chars": 7125,
"preview": "package com.funtester.frame.execute;\n\nimport com.funtester.base.bean.PerformanceResultBean;\nimport com.funtester.base.co"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/Progress.java",
"chars": 5433,
"preview": "package com.funtester.frame.execute;\n\nimport com.funtester.base.constaint.FixedQpsThread;\nimport com.funtester.base.cons"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/StatisticsUtil.java",
"chars": 6332,
"preview": "package com.funtester.frame.execute;\n\nimport com.funtester.config.Constant;\nimport com.funtester.utils.StringUtil;\nimpor"
},
{
"path": "src/main/groovy/com/funtester/frame/execute/ThreadPoolUtil.groovy",
"chars": 3180,
"preview": "package com.funtester.frame.execute;\n\nimport java.util.concurrent.*;\n\n/**\n * Java线程池Demo\n */\nclass ThreadPoolUtil {\n\n\n "
},
{
"path": "src/main/groovy/com/funtester/frame/thread/FixedQpsHeaderMark.groovy",
"chars": 1639,
"preview": "package com.funtester.frame.thread\n\nimport com.funtester.base.constaint.ThreadBase\nimport com.funtester.base.exception.P"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/FixedQpsParamMark.java",
"chars": 1090,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadBase;\nimport com.funtester.base.interface"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/HeaderMark.java",
"chars": 1864,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadBase;\nimport com.funtester.base.exception"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/ParamMark.java",
"chars": 1135,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadBase;\nimport com.funtester.base.interface"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/QuerySqlThread.java",
"chars": 1119,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadBase;\nimport com.funtester.base.constaint"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/RequestThreadTime.java",
"chars": 1682,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadLimitTimeCount;\nimport com.funtester.base"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/RequestThreadTimes.java",
"chars": 1746,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadLimitTimesCount;\nimport com.funtester.bas"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/RequestTimeFixedQps.java",
"chars": 1438,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.FixedQpsThread;\nimport com.funtester.base.inter"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/RequestTimesFixedQps.java",
"chars": 1446,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.FixedQpsThread;\nimport com.funtester.base.inter"
},
{
"path": "src/main/groovy/com/funtester/frame/thread/UpdateSqlThread.java",
"chars": 981,
"preview": "package com.funtester.frame.thread;\n\nimport com.funtester.base.constaint.ThreadBase;\nimport com.funtester.base.constaint"
},
{
"path": "src/main/groovy/com/funtester/httpclient/ClientManage.java",
"chars": 12613,
"preview": "package com.funtester.httpclient;\n\nimport com.funtester.base.exception.FailException;\nimport com.funtester.config.Consta"
},
{
"path": "src/main/groovy/com/funtester/httpclient/FunLibrary.java",
"chars": 19195,
"preview": "package com.funtester.httpclient;\n\nimport com.alibaba.fastjson.JSONException;\nimport com.alibaba.fastjson.JSONObject;\nim"
},
{
"path": "src/main/groovy/com/funtester/httpclient/FunRequest.groovy",
"chars": 10685,
"preview": "package com.funtester.httpclient\n\nimport com.alibaba.fastjson.JSONObject\nimport com.funtester.base.bean.RequestInfo\nimpo"
},
{
"path": "src/main/groovy/com/funtester/httpclient/GCThread.java",
"chars": 1081,
"preview": "package com.funtester.httpclient;\n\nimport com.funtester.config.HttpClientConstant;\n\nimport static com.funtester.frame.So"
},
{
"path": "src/main/groovy/com/funtester/main/ExecuteMethod.java",
"chars": 430,
"preview": "package com.funtester.main;\n\nimport com.funtester.frame.SourceCode;\nimport com.funtester.frame.execute.ExecuteSource;\nim"
},
{
"path": "src/main/groovy/com/funtester/main/PerformanceFromFile.groovy",
"chars": 1274,
"preview": "package com.funtester.main\n\nimport com.funtester.frame.SourceCode\nimport com.funtester.frame.execute.Concurrent\nimport c"
},
{
"path": "src/main/groovy/com/funtester/socket/ScoketIOFunClient.java",
"chars": 6410,
"preview": "package com.funtester.socket;\n\n/**\n * 基于Socket.IO的Client封装对象\n */\npublic class ScoketIOFunClient {\n//public class ScoketI"
},
{
"path": "src/main/groovy/com/funtester/socket/WebSocketFunClient.java",
"chars": 5375,
"preview": "package com.funtester.socket;\n\n/**\n * socket客户端代码,限于WebSocket协议的测试\n */\npublic class WebSocketFunClient {\n//public class "
},
{
"path": "src/main/groovy/com/funtester/utils/ArgsUtil.java",
"chars": 1340,
"preview": "package com.funtester.utils;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.funte"
},
{
"path": "src/main/groovy/com/funtester/utils/CMD.java",
"chars": 3290,
"preview": "package com.funtester.utils;\n\nimport com.funtester.config.Constant;\nimport org.apache.commons.lang3.StringUtils;\nimport "
},
{
"path": "src/main/groovy/com/funtester/utils/CountUtil.groovy",
"chars": 1279,
"preview": "package com.funtester.utils\n\nimport org.apache.logging.log4j.LogManager\nimport org.apache.logging.log4j.Logger\n\nimport j"
},
{
"path": "src/main/groovy/com/funtester/utils/CurlUtil.groovy",
"chars": 2749,
"preview": "package com.funtester.utils\n\nimport com.alibaba.fastjson.JSONObject\nimport com.funtester.config.Constant\nimport com.funt"
},
{
"path": "src/main/groovy/com/funtester/utils/DecodeEncode.java",
"chars": 6872,
"preview": "package com.funtester.utils;\n\nimport com.funtester.base.exception.FailException;\nimport com.funtester.config.Constant;\ni"
},
{
"path": "src/main/groovy/com/funtester/utils/FileUtil.groovy",
"chars": 2438,
"preview": "package com.funtester.utils\n\nimport com.funtester.config.Constant\nimport org.apache.logging.log4j.LogManager\nimport org."
},
{
"path": "src/main/groovy/com/funtester/utils/HeapDumper.java",
"chars": 1637,
"preview": "package com.funtester.utils;\n\nimport com.sun.management.HotSpotDiagnosticMXBean;\nimport org.apache.logging.log4j.LogMana"
},
{
"path": "src/main/groovy/com/funtester/utils/Join.java",
"chars": 1790,
"preview": "package com.funtester.utils;\n\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.StringUtils;\n\n"
},
{
"path": "src/main/groovy/com/funtester/utils/JsonUtil.groovy",
"chars": 4591,
"preview": "package com.funtester.utils\n/**下面是例子,官方文档地址:https://github.com/json-path/JsonPath/blob/master/README.md\n * $.store.book["
},
{
"path": "src/main/groovy/com/funtester/utils/RWUtil.java",
"chars": 7993,
"preview": "package com.funtester.utils;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.base.exception.FailException;"
},
{
"path": "src/main/groovy/com/funtester/utils/Regex.java",
"chars": 2729,
"preview": "package com.funtester.utils;\n\nimport com.funtester.base.exception.ParamException;\nimport com.funtester.frame.SourceCode;"
},
{
"path": "src/main/groovy/com/funtester/utils/StringUtil.groovy",
"chars": 7421,
"preview": "package com.funtester.utils\n\nimport com.funtester.frame.SourceCode\n\nimport java.util.stream.Collectors\n\n/**\n * 处理各种字符串的工"
},
{
"path": "src/main/groovy/com/funtester/utils/Time.java",
"chars": 5460,
"preview": "package com.funtester.utils;\n\nimport com.funtester.config.Constant;\nimport com.funtester.frame.SourceCode;\nimport org.ap"
},
{
"path": "src/main/groovy/com/funtester/utils/TimeWatch.groovy",
"chars": 6074,
"preview": "package com.funtester.utils\n\n\nimport com.funtester.frame.SourceCode\nimport org.apache.logging.log4j.LogManager\nimport or"
},
{
"path": "src/main/groovy/com/funtester/utils/WriteHtml.java",
"chars": 2044,
"preview": "package com.funtester.utils;\n\nimport java.util.List;\n\n/**\n * 生成表格封装类\n */\npublic class WriteHtml {\n\n /**\n * 获取表格头部"
},
{
"path": "src/main/groovy/com/funtester/utils/message/AlertOver.java",
"chars": 3098,
"preview": "package com.funtester.utils.message;\n\nimport com.funtester.base.bean.RequestInfo;\nimport com.funtester.base.interfaces.I"
},
{
"path": "src/main/groovy/com/funtester/utils/message/EmailUtil.java",
"chars": 2583,
"preview": "package com.funtester.utils.message;\n\nimport com.funtester.frame.SourceCode;\n\n/**\n * 发送邮件的类,暂时使用静态方法,默认使用QQ邮箱发送\n */\npubl"
},
{
"path": "src/main/groovy/com/funtester/utils/request/Request.java",
"chars": 6452,
"preview": "package com.funtester.utils.request;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.funtester.config.RequestType;\ni"
},
{
"path": "src/main/groovy/com/funtester/utils/request/RequestFile.java",
"chars": 2391,
"preview": "package com.funtester.utils.request;\n\nimport com.funtester.config.Constant;\nimport com.funtester.config.RequestType;\nimp"
},
{
"path": "src/main/groovy/com/funtester/utils/request/Swagger.java",
"chars": 5008,
"preview": "package com.funtester.utils.request;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimp"
},
{
"path": "src/main/groovy/com/funtester/utils/xml/Attr.groovy",
"chars": 349,
"preview": "package com.funtester.utils.xml\n/**\n * 节点属性信息\n */\nclass Attr {\n//class Attr extends AbstractBean implements Serializable"
},
{
"path": "src/main/groovy/com/funtester/utils/xml/NodeInfo.groovy",
"chars": 261,
"preview": "package com.funtester.utils.xml\n/**\n * 节点信息\n */\nclass NodeInfo {\n//class NodeInfo extends AbstractBean implements Serial"
},
{
"path": "src/main/groovy/com/funtester/utils/xml/XMLUtil.groovy",
"chars": 1931,
"preview": "package com.funtester.utils.xml\n/**\n * 基于dom解析xml文件工具类\n */\nclass XMLUtil {\n\n// private static Logger logger = LogMana"
},
{
"path": "src/main/groovy/com/funtester/utils/xml/XMLUtil2.groovy",
"chars": 1663,
"preview": "package com.funtester.utils.xml\n/**\n * 基于dom4j解析xml工具类\n */\nclass XMLUtil2 {\n\n// private static Logger logger = LogMan"
},
{
"path": "src/main/resources/http.properties",
"chars": 260,
"preview": "ssl_v=TLSv1.2\nUser-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/7"
},
{
"path": "src/main/resources/log4j2.xml",
"chars": 4658,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->\n<!--"
},
{
"path": "src/main/resources/mysql.properties",
"chars": 139,
"preview": "test_mysql_url=jdbc:mysql://172.18.4.55:3306/okayapi\nuser=root\npassword=dsjw2016\nmysql_server_path=http://172.18.4.55:88"
},
{
"path": "src/main/resources/redis.properties",
"chars": 89,
"preview": "ip=58.87.70.151\nport=6379\nmax_total=10\nmax_idle=5\nmin_idle=2\nmax_wait=60000\ntimeout=60000"
}
]
About this extraction
This page contains the full source code of the JunManYuanLong/FunTester GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 119 files (428.1 KB), approximately 141.7k tokens, and a symbol index with 572 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.