在做自动化测试之前你需要知道的

什么是自动化测?

       做测试好几年了,真正学习和实践自动化测试一年,自我感觉这一个年中收获许多。一直想动笔写一篇文章分享自动化测试实践中的一些经验。终于决定花点时间来做这件事儿。

  首先理清自动化测试的概念,广义上来讲,自动化包括一切通过工具(程序)的方式来代替或辅助手工测试的行为都可以看做自动化,包括性能测试工具(loadrunner、jmeter),或自己所写的一段程序,用于生成1到100个测试数据。狭义上来讲,通工具记录或编写脚本的方式模拟手工测试的过程,通过回放或运行脚本来执行测试用例,从而代替人工对系统的功能进行验证。

当然,我们更普遍的认识把“自动化测试”看做“ 基于产品或项目UI层的自动化测试”。

分层的自动化测试

       这个概念最近曝光度比较高,传统的自动化测试更关注的产品UI层的自动化测试,而分层的自动化测试倡导产品的不同阶段(层次)都需要自动化测试。1

  相信测试同学对上面的金字塔并不陌生,这不就是对产品开发不同阶段所对应的测试么!我们需要规范的来做单元测试同样需要相应的单元测试框架,如java的Junit、testNG,C#的NUnit ,python 的unittest、pytest 等,几乎所有的主流语言,都会有其对应的单元测试框架。

  集成、接口测试对于不少测试新手来说不太容易理解,单元测试关注代码的实现逻辑,例如一个if 分支或一个for循环的实现;那么集成、接口测试关注的一是个函数、类(方法)所提供的接口是否可靠。例如,我定义一个add()函数用于计算两个参数的结果并返回,那么我需要调用add()并传参,并比较返回值是否两个参数相加。当然,接口测试也可以是url的形式进行传递。例如,我们通过get方式向服务器发送请求,那么我们发送的内容做为URL的一部分传递到服务器端。但比如 Web service 技术对外提供的一个公共接口,需要通过soapUI 等工具对其进行测试。

  UI层的自动化测试,这个大家应该再熟悉不过了,大部分测试人员的大部分工作都是对UI层的功能进行测试。例如,我们不断重复的对一个表单提交,结果查询等功能进行测试,我们可以通过相应的自动化测试工具来模拟这些操作,从而解放重复的劳动。UI层的自动化测试工具非常多,比较主流的是QTP,Robot Framework、watir、selenium 等。

 为什么要画成一个金字塔形,则不是长方形 或倒三角形呢? 这是为了表示不同阶段所投入自动化测试的比例。如果一个产品从没有做单元测试与接口测试,只做UI层的自动化测试是不科学的,从而很难从本质上保证产品的质量。如果你妄图实现全面的UI层的自动化测试,那更是一个劳民伤财的举动,投入了大量人力时间,最终获得的收益可能会远远低于所支付的成本。因为越往上层,其维护成本越高。尤其是UI层的元素会时常的发生改变。所以,我们应该把更多的自动化测试放在单元测试与接口测试阶段进行。

  既然UI层的自动化测试这么劳民伤财,那我们只做单元测试与接口测试好了。NO! 因为不管什么样的产品,最终呈现给用户的是UI层。所以,测试人员应该更多的精力放在UI层。那么也正是因为测试人员在UI层投入大量的精力,所以,我们有必要通过自动化的方式帮助我们“部分解放”重复的劳动。

  在自动化测试中最怕的是变化,因为变化的直接结果就是导致测试用例的运行失败,那么就需要对自动化脚本进行维护;如何控制失败,降低维护成本对自化的成败至关重要。反过来讲,一份永远都运行成功的自动化测试用例是没有价值。

  至于在金字塔中三种测试的比例要根据实际的项目需求来划分。在《google 测试之道》一书,对于google产品,70%的投入为单元测试,20%为集成、接口测试,10% 为UI层的自动化测试。

我为什么要做自动化测试?

   根据51testing的《中国软件测试从业人员调查报告》,手工测试占到的89% ,相对开发来说,测试的门槛底,薪资普遍较底,所要求的知识面虽然有一定广度,但缺乏深度。这是测试的普遍现状。

  正因为手功测试人门槛不高,使大量的毕业生,甚至是非专业人员涌入这个行业。从而增加了这个行业的激烈竞争。对于工作几年扔处于手工测试的人员来说都会有强列的危机感。由于工作的技术含量不高,薪资的涨幅遇到瓶颈,另一方面受到新进入者的威胁,同样的工作公司花5K招来的人就可以做,那么就不会花8K 的招。

  好吧,这个问题不应该出现讨论技术的话题中,但他的确是大多测试人员不得不面对的一个问题。所以,从测试人员自身的发展来说,我其实非常需要通过自动化技术来增加自己有竞争力。当然,做到一定年限测试人员会选择转管理或其它岗位,这又是另一个话题了。

  从测试行业的发展来说,国内产品由于产品特点,世界级的产品不多,技术含量相对不高,质量要求相对要求不高,外包国外项目,测试人力成本低廉,所以需要大量的手工测试人员。

  所以,在不远的未来,我认为纯的工手测试人员的需求是递减,公司更需要更高技术能力的测试。质量需要测试,测试行为永远不会消失,但纯的手工测试人员是否消失是有可能的。

  好吧,你可以说测试多朝阳的行业,我纯属在危言耸听。不管未来如何,我们都需要提升自身的技能对吧!

什么项目适合做自动化测试?

  假如你已经决定要学习自动化测试了,如何学习是要面临的下一个问题?这个问题以被测试产品为出发点进行分析,假如你所学的技术不能得到应用(验证),将会使你的学习过程寸步难行。

  首先考考虑产品是否适合做自动化测试。这方法比较普遍的共识是从三个方面进行权衡。

  软件需求变动不频繁

  测试脚本的稳定性决定了自动化测试的维护成本。如果软件需求变动过于频繁,测试人员需要根据变动的需求来更新测试用例以及相关的测试脚本,而脚本的维护本身就是一个代码开发的过程,需要修改、调试,必要的时候还要修改自动化测试的框架,如果所花费的成本不低于利用其节省的测试成本,那么自动化测试便是失败的。

  项目中的某些模块相对稳定,而某些模块需求变动性很大。我们便可对相对稳定的模块进行自动化测试,而变动较大的仍是用手工测试。

  项目周期较长

由于自动化测试需求的确定、自动化测试框架的设计、测试脚本的编写与调试均需要相当长的时间来完成。这样的过程本身就是一个测试软件的开发过程,需要较长的时间来完成。如果项目的周期比较短,没有足够的时间去支持这样一个过程,那么自动化测试便成为笑谈。

  自动化测试脚本可重复使用

  自动化测试脚本的重复使用要从三个方面来考量,一方面所测试的项目之间是否很大的差异性(如C/S系统和B/S系统的差异);所选择的测试工具是否适应这种差异;最后,测试人员是否有能力开发出适应这种差异的自动化测试框架。

选择什么工具进行自动化测试

  假如你已经确认了XX 项目适合做自动化测试,那么接下来你要做的就是选测试工具了。

  首先要先确认你所测试的产品是桌面程序(C/S)还是web应用(B/S)。

  桌面程序的工具有:QTP、 AutoRunner

  web应用的工具有:QTP、AutoRunner、Robot Framework、watir、selenium

  由于B/S架构的诸多优势,早几年前大量C/S架构的应用转为B/S结构。从而也推动了web开发与测试技术的发展。假如,被测试有产品是C/S架构的,那么推荐QTP ,QTP在UI自动化测试领域占到了一半的试用率。所以,足以说明QTP在自动化领域强大,易用性等。学习主流的工具也可以使你获得更多的机会。市面上关于QTP的书籍也非常丰富。当然,要想学好QTP ,你必须要掌握VBS脚本语言。

  如果,被测产品是B/S 结构,那么推荐selenium ,为什么不是QTP 或其它工具?因为selenium 对B/S应用支持很好,更重要的一点,它支持多语言的开发,真正的试用selenium ,你所要掌握的不仅仅是一个工具而已,你还需要学习一门语言。我为什么要选择selenium?还要学一门语言,这无疑增加了我的学习成本。增加成本的同时,也增加的你的竞争力,而且,在这个过程中你不单单只是学会了一个自动化工具而已,你完全可以使用所学的语言去做更多的事情。

  好吧!假如你决定试用selenium 了之后,你又面临了一个新的问题,选择一门语言。selenium 是支持java、python、ruby、php、C#、JavaScript 。

  从语言易学性来讲,首选ruby ,python

  从语言应用广度来讲,首选java、C#、php、

  从语言相关测试技术成度(及 资料)来讲:ruby ,python ,java

  或者你可以考虑整个技术团队主流用什么语言,然后选择相应的语言。

selenium 用前须知

  OK!经过上的过程,我相信你一定做出的相应的选择,如果你选择的是selenium 工具,那么接着往下阅读。

首选你在开始selenium之前,需要花一到两个月时间去学一门语言,这里是根据没有语言基础的同学而定的。我推荐ruby ,python ,java 任意一门语言来进行学习。

  当然,已经如果有很好的语言基础略过这个环节,或者你的丰富的java编程能力,那么学习python 可能只需要几天时间或更短。

  假如,你已经搞定了一门语言的基础,接下来你需要先了解selenium ,selenium 并不是单纯的一个工具,他是一组工具的集合,而且,他还有1.0与2.0之分,当然3.0也已经到来。

  selenium 也不是简单一个工具,而是由几个工具组成,每个工具都有其特点和应用场景。

2

selenium IDE

  selenium IDE 是嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作的录制与回放功能。那么什么情况下用到它呢?

  快速的创建bug重现脚本,在测试人员的测试过程中,发现了bug之后可以通过IDE将重现的步骤录制下来,以帮助开发人员更容易的重现bug。

  IDE录制的脚本可以可以转换成多种语言,从而帮助我们快速的开发脚本,关于这个功能后而用到时再详细介绍。

selenium Grid

  Selenium Grid是一种自动化的测试辅助工具,Grid通过利用现有的计算机基础设施,能加快Web-app的功能测试。利用Grid,可以很方便地同时在多台机器上和异构环境中并行运行多个测试事例。其特点为:

· 并行执行

· 通过一个主机统一控制用例在不同环境、不同浏览器下运行。

· 灵活添加变动测试机

selenium RC

  selenium RC 是selenium 家族的核心工具,selenium RC 支持多种不同的语言编写自动化测试脚本,通过selenium RC 的服务器作为代理服务器去访问应用从而达到测试的目的。

  selenium RC 使用分Client Libraries和selenium Server,Client Libraries库主要主要用于编写测试脚本,用来控制selenium Server的库。

  Selenium Server负责控制浏览器行为,总的来说,Selenium Server主要包括3个部分:Launcher、Http Proxy、Core。其中Selenium Core是被Selenium Server嵌入到浏览器页面中的。其实Selenium Core就是一堆JS函数的集合,就是通过这些JS函数,我们才可以实现用程序对浏览器进行操作。Launcher用于启动浏览器,把selnium Core加载到浏览器页面当中,并把浏览器的代理设置为Selenium Server 的Http Proxy。

selenium 2.0

  搞清了selenium 1.0 的家族关系,selenium 2.0 是把WebDriver 加入到了这个家族中;简单用公式表示为:

  selenium 2.0 = selenium 1.0 + WebDriver

  需要强调的是,在selenium 2.0 中主推的是WebDriver ,WebDriver 是selenium RC 的替代品,因为 selenium 为了向下兼容性,所以selenium RC 并没有彻底抛弃,如果你使用selenium开发一个新自动化测试项目,强列推荐使用WebDriver 。那么selenium RC 与webdriver 主要有什么区别呢?

  selenium RC 在浏览器中运行JavaScript应用,使用浏览器内置的JavaScript 翻译器来翻译和执行selenese命令(selenese 是selenium命令集合)。

  WebDriver通过原生浏览器支持或者浏览器扩展直接控制浏览器。WebDriver针对各个浏览器而开发,取代了嵌入到被测Web应用中的JavaScript。与浏览器的紧密集成支持创建更高级的测试,避免了JavaScript安全模型导致的限制。除了来自浏览器厂商的支持,WebDriver还利用操作系统级的调用模拟用户输入。

  如果是新项目直接学习webdriver 就OK了,RC是过时技术。

selenium学习路线

  配置你的测试环境,真对你所学习语言,来配置你相应的selenium 测试环境。selenium 好比定义的语义—“问好”,假如你使用的是中文,为了表术问好,你的写法是“你好”,假如你使用的是英语,你的写法是“hello”。 所以,同样有语义在不同的语言下会有不同的写法(语法)。

   接着你需要熟悉webdriver API ,API就是selenium 所定义一方法,用于定位,操作页面上的各种元素。

  先学习元素的定位,selenium 提供了id、name、class name、 tag name、link text、partial link text、 xpath、css、等定位方法。xpath和css 功能强大语法稍微复杂,在这其间你可能还需要了解更多的前端知识。xml ,javascript 等。

  定位元素的目的是为了操作元素,接就要学习各种元素有操作,输入框,下拉框,按钮点击,文件上传、下载,分页,对话框,警告框…等等。

  经过一段时间的学习,你可以游刃有余的模拟手工测试来操作页面上的各种元素了。接着你需要做的就是把这些“用例”组织起来,统一来跑。

  那么你需要做的就是学习并使用单元测试框架,单元测试框架本身就解决了用例的组织与运行。

  当你写了一些“测试用例” 之后,你会发现用例中有大量重复的操作,能不能写到一个单独的文件中,需要的时候调用这些操作?当然可以,运用你的编程能力来实现这一点将非常简单。然后,你又发现每个用例中都有一些数据,这些数据也是一样的,但如果变化了修改起来非常麻烦,你也可以把他写到一个单独的文件中进行读取。

  接着你又遇到了新的疑问,我写的脚本(用例)都是流水式的,我怎么知道用例运行失败还是成功。那么就需要在脚本中加一些验证与断言。

  接着你又有了更多的想法,单元测试框架的log太简陋了,能不能生成一张漂亮的测试报告出来。我能不能定时的来跑这个脚本。能不能把每一次跑脚本的测试结果直接发到我的邮箱。能不能……

  为解决这些问题,你不得不学习更多的编程技术,然后你的“测试结构”会功能越来越强大,越来越灵活。产生了一定的通用性和移植性。一个有模有样的自动化测试框架诞生了。

   假如,有一天你不再做UI的自动化测试了,你会发现你去做单元测试 或接口测试基本没什么难度。开发个测试工具之类的也不在话下,感谢selenium 吧!顺便也感谢一下我吧!

总结:每天学习一点点!

ELK使用及详解

1.前言

通常,日志被分散的储存不同的设备上。如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志。这样是不是感觉很繁琐和效率低下。当务之急我们使用集中化的日志管理,例如:开源的 syslog ,将所有服务器上的日志收集汇总。

集中化管理日志后,日志的统计和检索又成为一件比较麻烦的事情,一般我们使用grep 、 awk 和 wc 等 Linux 命令能实现检索和统计,但是对于要求更高的查询、排序和统计等要求和庞大的机器数量依然使用这样的方法难免有点力不从心。

开源实时日志分析 ELK 平台能够完美的解决我们上述的问题, ELK 由ElasticSearch 、 Logstash 和 Kiabana 三个开源工具组成。官方网站:https://www.elastic.co/products

l   Elasticsearch 是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制, restful 风格接口,多数据源,自动搜索负载等。

l   Logstash 是一个完全开源的工具,他可以对你的日志进行收集、分析,并将其存储供以后使用(如,搜索)。

l   kibana 也是一个开源和免费的工具,他 Kibana 可以为 Logstash 和 ElasticSearch提供的日志分析友好的 Web 界面,可以帮助您汇总、分析和搜索重要数据日志。

工作原理如下如所示:

在需要收集日志的所有服务上部署 logstash ,作为 logstash agent ( logstash shipper )用于监控并过滤收集日志,将过滤后的内容发送到 logstash indexer ,logstash indexer 将日志收集在一起交给全文搜索服务 ElasticSearch ,可以用ElasticSearch 进行自定义搜索通过 Kibana 来结合自定义搜索进行页面展示。

2.kibana查询语法

我们kibana页面访问地址:elk.dev.aixuexi.com,下图是查询页面上常用的功能注释微信图片_20170912191526

 单项term查询

例: 搜independenceDay,student等(不建议这么查询慢,容易超时)

字段field查询

field:value   例:appname:Alen, host:beijing

通配符

? 匹配单个字符      例: H?bbs

* 匹配0到多个字符           例: H*

注意: ? * 不能用作第一个字符,例如: ?text    *text

范围查询

age:[20 TO 30]        age:{20 TO 30}

注:[ ] 表示端点数值包含在范围内,{ } 表示端点数值不包含在范围内

逻辑操作(建议使用查询快)

AND     OR       例子:firstname:H* AND age:20          firstname:H* OR age:20

+ :搜索结果中必须包含此项

– :不能含有此项

例: +firstname:H* -age:20 city:H*    firstname字段结果中必须存在H开头的,不能有年龄是20的,city字段H开头的可有可无

分组

(firstname:H* OR age:20) AND state:KS      先查询名字H开头年龄或者是20的结果,然后再与国家是KS的结合

字段分组

firstname:(+H* -He*)        搜索firstname字段里H开头的结果,并且排除firstname里He开头的结果

转义特殊字符

+ – && || ! () {} [] ^” ~ * ? : \

注意:以上字符当作值搜索的时候需要用 \ 转义(例如account-service搜索时加引号或者转义)

3.logstash配置

工作原理:
每个阶段都由很多的插件配合工作,比如file、elasticsearch、redis等等。
每个阶段也可以指定多种方式,比如输出既可以输出到elasticsearch中,也可以指定到stdout在控制台打印。
 
命令行中常用的命令
 
    -f:通过这个命令可以指定Logstash的配置文件,根据配置文件配置logstash
    -e:后面跟着字符串,该字符串可以被当做logstash的配置(如果是“” 则默认使用stdin作为输入,stdout作为输出)
    -l:日志输出的地址(默认就是stdout直接在控制台中输出)
    -t:测试配置文件是否正确,然后退出。
 
 
logstash基本上由三部分组成:
 
    input {…}
    filter {…}
    output {…}
 
在每个部分中,也可以指定多个访问方式,例如多个日志来源:
 
    input { 
     file { path =>”/var/log/messages” type =>”syslog”} 
     file { path =>”/var/log/apache/access.log” type =>”apache”} 
    }
  类似的,如果在filter中添加了多种处理规则,则按照它的顺序一一处理,但是有一些插件并不是线程安全的。
  比如在filter中指定了两个一样的的插件,这两个任务并不能保证准确的按顺序执行,因此官方也推荐避免在filter中重复使用插件。
 
例子:
 
    input {
        file {
         #指定监听的文件路径,注意必须是绝对路径
            path => “E:/software/logstash-1.5.4/logstash-1.5.4/data/test.log”
            start_position => beginning
        }
    }
    filter {
        
    }
    output {
        stdout {}
    }
日志大致如下:
 
    hello,this is first line in test.log!
    hello,my name is xingoo!
    goodbye.this is last line in test.log!
 
我们的配置文件
input {
        file {
                #start_position => beginning
                #add_field => {“project_name” => “battleship”}
                #tags => “tag1”
                path => [“/data/var/log/application/*/*/*.log”]
        }
}
filter {
        grok {
                patterns_dir => [“/usr/local/logstash-2.3.4/patterns”]
                match => { “message” => “%{TIMESTAMP_ISO8601:logdate} %{LOGLEVEL:level}”
                        }
             }
        grok {
                match => {
                        “path” => “/data/var/log\/application/%{USER:appname}/”
                        }
             }
}
output {
        elasticsearch{
         hosts => [“10.44.202.185:9200″,”10.28.51.196:9200″,”10.28.51.196:9200”]
     }
     stdout { codec => rubydebug }
        }
4.elasticsearch增删改查详解
刚接触的童鞋经常搞不明白ES的各个概念的含义。尤其对“索引”二字更是与关系型数据库混淆的不行。本文通过对比关系型数据库,将ES中常见的增、删、改、查操作进行图文呈现。能加深你对ES的理解。同时,也列举了kibana下的图形化展示。

ES Restful API GET、POST、PUT、DELETE、HEAD含义:
1)GET:获取请求对象的当前状态。
2)POST:改变对象的当前状态。
3)PUT:创建一个对象。
4)DELETE:销毁对象。
5)HEAD:请求获取对象的基础信息。

MySQL与Elasticsearch核心概念对比示意图
这里写图片描述
以上表为依据,
ES中的新建文档(在Index/type下)相当于mysql中(在某Database的Table)下插入一行数据。

1、新建文档(类似mysql insert插入操作)

http://localhost:9200/blog/ariticle/1 put
{
"title":"New version of Elasticsearch released!",
"content":"Version 1.0 released today!",
"tags":["announce","elasticsearch","release"]
}

创建成功如下显示:

{

- "_index": "blog",
- "_type": "ariticle",
- "_id": "1 -d",
- "_version": 1,
- "_shards": {
    - "total": 2,
    - "successful": 1,
    - "failed": 0
- },
- "created": true

}

这里写图片描述

2、检索文档(类似mysql search 搜索select*操作)

检索结果如下:

{

- "_index": "blog",
- "_type": "ariticle",
- "_id": "1",
- "_version": 1,
- "found": true,
- "_source": {
    - "title": "New version of Elasticsearch released!",
    - "content": "Version 1.0 released today!",
    - "tags": [
        - "announce"
        - ,
        - "elasticsearch"
        - ,
        - "release"
    - ]
- }

}

如果未找到会提示:

{

- "_index": "blog",
- "_type": "ariticle",
- "_id": "11",
- "found": false

}

查询全部文档如下:
这里写图片描述
具体某个细节内容检索,
查询举例1:查询cotent列包含版本为1.0的信息。
http://localhost:9200/blog/
_search?pretty&q=content:1.0

{

- "took": 2,
- "timed_out": false,
- "_shards": {
    - "total": 5,
    - "successful": 5,
    - "failed": 0
- },
- "hits": {
    - "total": 1,
    - "max_score": 0.8784157,
    - "hits": [
        - {
            - "_index": "blog",
            - "_type": "ariticle",
            - "_id": "6",
            - "_score": 0.8784157,
            - "_source": {
                - "title": "deep Elasticsearch!",
                - "content": "Version 1.0!",
                - "tags": [
                    - "deep"
                    - ,
                    - "elasticsearch"
                - ]
            - }
        - }
    - ]
- }

}

查询举例2:查询书名title中包含“enhance”字段的数据信息:
[root@5b9dbaaa1a ~]# curl -XGET 10.200.1.121:9200/blog/ariticle/_search?pretty -d ‘

> { "query" : {
> "term" :
> {"title" : "enhance" }
> }
> }'
{
  "took" : 189,
  "timed_out" : false,
  "_shards" : {
  "total" : 5,
  "successful" : 5,
  "failed" : 0
  },
  "hits" : {
  "total" : 2,
  "max_score" : 0.8784157,
  "hits" : [ {
  "_index" : "blog",
  "_type" : "ariticle",
  "_id" : "4",
  "_score" : 0.8784157,
  "_source" : {
  "title" : "enhance Elasticsearch!",
  "content" : "Version 4.0!",
  "tags" : [ "enhance", "elasticsearch" ]
  }
  }, {
  "_index" : "blog",
  "_type" : "ariticle",
  "_id" : "5",
  "_score" : 0.15342641,
  "_source" : {
  "title" : "enhance Elasticsearch for university!",
  "content" : "Version 5.0!",
  "tags" : [ "enhance", "elasticsearch" ]
  }
  } ]
  }
}

查询举例3:查询ID值为3,5,7的数据信息:
[root@5b9dbaaa148a ~]# curl -XGET 10.200.1.121:9200/blog/ariticle/_search?pretty -d ‘

{ "query" : {
"terms" :
{"_id" : [ "3", "5", "7" ] }
}
}'
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
  "total" : 5,
  "successful" : 5,
  "failed" : 0
  },
  "hits" : {
  "total" : 3,
  "max_score" : 0.19245009,
  "hits" : [ {
  "_index" : "blog",
  "_type" : "ariticle",
  "_id" : "5",
  "_score" : 0.19245009,
  "_source" : {
  "title" : "enhance Elasticsearch for university!",
  "content" : "Version 5.0!",
  "tags" : [ "enhance", "elasticsearch" ]
  }
  }, {
  "_index" : "blog",
  "_type" : "ariticle",
  "_id" : "7",
  "_score" : 0.19245009,
  "_source" : {
  "title" : "deep Elasticsearch for university!",
  "content" : "Version 2.0!",
  "tags" : [ "deep", "elasticsearch", "university" ]
  }
  }, {
  "_index" : "blog",
  "_type" : "ariticle",
  "_id" : "3",
  "_score" : 0.19245009,
  "_source" : {
  "title" : "init Elasticsearch for university!",
  "content" : "Version 3.0!",
  "tags" : [ "initialize", "elasticsearch" ]
  }
  } ]
  }
}

3、更新文档(类似mysql update操作)

http://localhost:9200/blog/ariticle/1/_update/ POST
{“script”:”ctx._source.content = \”new version 2.0 20160714\”“}

更新后结果显示:
{

  • “_index”: “blog”,
  • “_type”: “ariticle”,
  • “_id”: “1”,
  • “_version”: 2,
  • “_shards”: {
    • ”total”: 2,
    • “successful”: 1,
    • “failed”: 0
  • }

}

查询&验证更新后结果:(对比可知,版本号已经更新完毕)
http://localhost:9200/blog/ariticle/1/

{

- "_index": "blog",
- "_type": "ariticle",
- "_id": "1",
- "_version": 2,
- "found": true,
- "_source": {
    - "title": "New version of Elasticsearch released!",
    - "content": "new version 2.0 20160714",
    - "tags": [
        - "announce"
        - ,
        - "elasticsearch"
        - ,
        - "release"
    - ]
- }

}
`![这里写图片描述](http://img.blog.csdn.net/20160717132407353)``

注意更新文档需要在elasticsearch_win\config\elasticsearch.yml下新增以下内容:

script.groovy.sandbox.enabled: true
script.engine.groovy.inline.search: on
script.engine.groovy.inline.update: on
script.inline: on
script.indexed: on
script.engine.groovy.inline.aggs: on
index.mapper.dynamic: true

4、删除文档(类似mysql delete操作

http://localhost:9200/blog/ariticle/8/回结果

{

- "found": true,
- "_index": "blog",
- "_type": "ariticle",
- "_id": "8",
- "_version": 2,
- "_shards": {
    - "total": 2,
    - "successful": 1,
    - "failed": 0
- }

}

这里写图片描述

 

基础算法之动态规划

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。
把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,属于过程优化的范畴,在现在的很多程序处理过程中广泛运用,直接正题吧,概念性的可以百度更深入了解,
今天我先来点简单的例子:

来一道题:找出给定字符串中的最长回文串;
回文串:即对称串,比如aaabbb、adda、a,除了字符本身外,回文串一定是偶数个字符,是基于中心对称的.
我们试试最常规的写法(暴力):
遍历每一个起始位置,再遍历每一个结束位置,再遍历每一个起始到结束位置的字符判断是不是回文,balabalalalalalalalla….时间复杂度O(n^3)
还是放弃吧…

下面看看动态规划的处理方式:
dp[i][j]=0表示子串[i,j]不是回文串。dp[i][j]=1表示子串[i,j]是回文串
则:
状态转移方程:dp[i,j]{=dp[i+1,j-1],if(s[i]==s[j]) || =0,if(s[i]!=s[j])}

我们把当前状态转移到了他的子集,这样就好处理了许多;

public static void main(String[] args) {
String s = “aaabbbbbbaaasss”;
if(s.length() <=1) //字符长度小于等于1,直接输出
System.out.println(s);
int maxLength = 0;
int start = 0;//记录最长回文串的开始位置
int[][] dp = new int[s.length()][s.length()];//二维数组,用于状态转移
for (int i = 0; i < s.length(); i++) {//初始化能保证[i,i]第i个位置到第i个位置即单个字符一定是回文串,如果[i,i+1]也等的话也一并初始化
dp[i][i] = 1;
if(i<s.length()-1 && s.charAt(i)==s.charAt(i+1))
{
dp[i][i+1]=1;
start=i;
maxLength=2;
}
}
for (int l = 3; l <= s.length(); l++) {//遍历所有的子串长度
for (int i = 0; i <= s.length()-l; i++) {//遍历所有长度的起始位置
int j = i + l – 1;//那么计算出子串的结束位置就该是它了
if(dp[i+1][j-1] == 1 && s.charAt(i)==s.charAt(j)){//对每个子串向前回溯,如果当前的起止位置字符相等并且向里缩进的子串是回文串,那么当前的起止串一定也是回文串
dp[i][j]=1;//当前的起止串标记为回文串
maxLength=l;//记录最大长度(如果有更长的,之前的就会被覆盖)
start=i;//记录每个长度的起始位置
}
}
}
if(maxLength >= 2)
System.out.println(s.substring(start,maxLength));
else
System.out.println(“没有最长回文!!!”);
}
动态规划处理这个问题的时间复杂度O(n^2),比之前快了很多,但是这个例子不够形象,我们从最简单的地方入手:

我们看一个数塔问题:TIM图片20170913144601

图片不清晰,再描述下问题:要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
状态转移方程dp [i] = max(dp[左孩子] , dp[右孩子]) + dp[i]
public static void main(String[] args) {
int[][] dp= {//初始化数塔
{9,0,0,0,0},
{12,15,0,0,0},
{10,6,8,0,0},
{2,18,9,5,0},
{19,7,10,4,16}
};
for(int i = 4; i > 0; i–) {
for (int j = 0; j < i; j++) {
dp[i – 1][j] += dp[i][j] > dp[i][j + 1] ? dp[i][j] : dp[i][j + 1];//如果左边比右边大,那么只加左孩子,反之加右孩子
//每次加完之后的值都会赋给上一层,直到dp[0][0],这样总能保证和最大
}
}
System.out.println(dp[0][0]);
}

暂时就这些吧,下次再补充~~~

mysql分表分页

之前涛哥建议用代码拼接的方式去分表分页,而不是sql拼接(不要union,也不要list分页),我就这么做了,贴一下代码分享下,有点乱,有更好的建议欢迎提出:
详细代码参考(axxBank)com.aixuexi.axxBank.dao.IncomeRecordDetailDao#getActualAccountInfoByCondition

List<IncomeRecordDetailInfo> list = new ArrayList<IncomeRecordDetailInfo>();
DateFormat df = new SimpleDateFormat(DateConstant.FORMAT_PATTERN_YMD1);
List<String> dateList = FinaicialCommon.getDateArrayByDate(actualAccountParams.getStartDate(), actualAccountParams.getEndDate());//对接入的起始日期作处理,决定接下来要查哪些表
int startIndex = (pageBean.getPageNum() – 1) * pageBean. getPagesize();//分页的开始位置
int endIndex = pageBean.getPageNum() * pageBean.getPagesize();//分页的结束位置
int sum = 0,start,end;//sum(记录开始到当前的总结果数) start(limit的开始位置) end(limit的大小)
Map<String, Integer> mapCount = getIncomeCountList(actualAccountParams, dictionaryIds, dateList, df);//获取每个分表对应的结果数 以及总结果数 (key:dateString value:各分表的结果数)
for (int date = dateList.size() – 1; date >= 0; date –) {//默认按时间倒序
String dateString = dateList.get(date);
String tableIncomeString = new StringBuilder(“income_record_”).append(dateString).toString();//分表名
String tableAmountString = new StringBuilder(“amount_record_”).append(dateString).toString();//分表名
StringBuilder sb = 。。。。//对分表的sql查询,我就不贴代码了,可以到对应的代码处查看
if(isPage) {//是否需要分页()
int resultCount = mapCount.get(dateString) == null ? 0 : mapCount.get(dateString);//每次(每个分表)查询的结果数
/**以下是分表分页算法,用于拼接分表之后的分页查询结果**/
sum += resultCount;//记录当前的结果总数,用于分页拼接
if (sum >= startIndex) {//sum 当前的结果总数 startIndex 分页的开始位置
if (sum > endIndex) {//当前结果总数是否大于分页的结束位置(当前表已经满足分页请求了)
start = startIndex – (sum – resultCount) < 0 ? 0 : (startIndex – (sum – resultCount)); //计算查询当前表的开始位置 如果为负置为0
end = startIndex – (sum – resultCount) + pageBean.getPagesize();//查询当前表的结束位置
sb.append(” LIMIT “).append(start).append(“,”).append(end-start);//对当前表指定分页,分页大小直接pageSize也行
list.addAll(jdbcTemplate.query(sb.toString(), new IncomeRecordDetailMapper()));
break;
} else if (sum <= endIndex) {//当前结果总数小于 分页的结束位置(当前表不满足分页请求,拼接完当前表,则继续拼接下一个表)
start = (startIndex – (sum – resultCount)) < 0 ? 0 : (startIndex – (sum – resultCount));//当前表的开始位置 如果为负置0
end = resultCount; //当前表的结束位置,结束位置直接是当前表的总结果数
sb.append(” LIMIT “).append(start).append(“,”).append(end);//当前表指定分页
list.addAll(jdbcTemplate.query(sb.toString(), new IncomeRecordDetailMapper()));
}
}
}else {//不需要分页的话直接拼接所有结果
list.addAll(jdbcTemplate.query(sb.toString(),new IncomeRecordDetailMapper()));
}
}

Jenkins的安装和简单配置(二)

Jenkins的安装和简单配置
1、下载一个jenkins文件(我这里用的是jenkins.msi文件),双击打开后,执行安装,配置自己要安装的路径。完成后自动打开浏览器,输入指定的秘钥(存在安装目录中,可获取),之后进入如下图进行选择安装插件。
clipboard
正在安装中。
2
jenkins之所以强大,不仅是因为功能强大,更重要的是能支持的插件非常之多,这就导致我们在使用某些功能的时候,下载相对应插件就能实现。
安装一些常用的推荐的插件成功后显示这个页面
3
输入需要设定的用户名和密码以及其他信息后,点击右下角蓝色按钮,页面跳转至jenkins已经登录的页面
4
我们也可以手动采用打开浏览器输入http://localhost:8080 本地登录jenkins,出现如上界面,说明jenkins基本的环境已经安装成功了。
2、一些简单的配置
1>我们所用到的插件的配置
点击左边菜单,系统管理,页面显示有插件管理
5
点击插件管理,显示如下界面
6
现在是已安装的界面,就是我们在开始时候默认安装的插件
7
红框中的插件就是我们用到的git插件。在jenkins项目中源码管理中显示这样。
8
现在来安装一个插件步骤:
点击可选插件
9
在过滤框中如需要安装的插件名称
10
勾选,点击立即获取,跳转显示该插件在安装中,安装完成后显示完成
11
其余插件安装方式与此相同
2>系统管理中还有一个较为常用的是管理用户
12
点击进去,跳转后,点击左侧新建用户,可为该jenkins添加其他用户
13
今天就到这里,下次再续…

testNG注解

1. TestNG的基本注解

@BeforeSuite 注解的方法将只运行一次,运行所有测试前此套件中。
@AfterSuite 注解的方法将只运行一次此套件中的所有测试都运行之后。
@BeforeClass 注解的方法将只运行一次先行先试在当前类中的方法调用。
@AfterClass 注解的方法将只运行一次后已经运行在当前类中的所有测试方法。
@BeforeTest 注解的方法将被运行之前的任何测试方法属于内部类的 标签的运行。
@AfterTest 注解的方法将被运行后,所有的测试方法,属于内部类的标签的运行。
@BeforeGroups 组的列表,这种配置方法将之前运行。此方法是保证在运行属于任何这些组第一个测试方法,该方法被调用。
@AfterGroups 组的名单,这种配置方法后,将运行。此方法是保证运行后不久,最后的测试方法,该方法属于任何这些组被调用。
@BeforeMethod 注解的方法将每个测试方法之前运行。
@AfterMethod 被注释的方法将被运行后,每个测试方法。
@DataProvider 标志着一个方法,提供数据的一个测试方法。注解的方法必须返回一个Object[] [],其中每个对象[]的测试方法的参数列表中可以分配。
该@Test 方法,希望从这个DataProvider的接收数据,需要使用一个dataProvider名称等于这个注解的名字。
@Factory 作为一个工厂,返回TestNG的测试类的对象将被用于标记的方法。该方法必须返回Object[]。
@Listeners 定义一个测试类的监听器。
@Parameters 介绍如何将参数传递给@Test方法。
@Test 标记一个类或方法作为测试的一部分。

2.注解实例

import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.AfterSuite;

public class TestngAnnotation {
// test case 1
@Test
public void testCase1() {
System.out.println(“in test case 1”);
}

// test case 2
@Test
public void testCase2() {
System.out.println(“in test case 2”);
}

@BeforeMethod
public void beforeMethod() {
System.out.println(“in beforeMethod”);
}

@AfterMethod
public void afterMethod() {
System.out.println(“in afterMethod”);
}

@BeforeClass
public void beforeClass() {
System.out.println(“in beforeClass”);
}

@AfterClass
public void afterClass() {
System.out.println(“in afterClass”);
}

@BeforeTest
public void beforeTest() {
System.out.println(“in beforeTest”);
}

@AfterTest
public void afterTest() {
System.out.println(“in afterTest”);
}

@BeforeSuite
public void beforeSuite() {
System.out.println(“in beforeSuite”);
}

@AfterSuite
public void afterSuite() {
System.out.println(“in afterSuite”);
}

}

接下来,让我们创建的文件 testng.xml 在 C:\ > TestNG_WORKSPACE 执行注解。









设计功能和界面测试用例

1.1 文本框、按钮等控件测试

1.1.1 文本框的测试

如何对文本框进行测试

a,输入正常的字母或数字。
b,输入已存在的文件的名称;
c,输入超长字符。例如在“名称”框中输入超过允许边界个数的字符,假设最多255个字符,尝试输入 256个字符,检查程序能否正确处理;
d,输入默认值,空白,空格;
e,若只允许输入字母,尝试输入数字;反之;尝试输入字母;
f,利用复制,粘贴等操作强制输入程序不允许的输入数据;
g,输入特殊字符集,例如,NUL及\n等;
h,输入超过文本框长度的字符或文本,检查所输入的内容是否正常显示;
i,输入不符合格式的数据,检查程序是否正常校验,如,程序要求输入年月日格式为yy/mm/dd,实际输入yyyy/mm/dd,程序应该给出错误提示

在测试过程中所用到的测试方法:

1,输入非法数据;
2,输入默认值;
3,输入特殊字符集;
4,输入使缓冲区溢出的数据;
5,输入相同的文件名;

命令按钮控件的测试

测试方法:

a,点击按钮正确响应操作。如,单击确定,正确执行操作;单击取消,退出窗口;
b,对非法的输入或操作给出足够的提示说明,如,输入月工作天数为32时,单击”确定“后系统应提示:天数不能大于31;
c,对可能造成数据无法恢复的操作必须给出确认信息,给用户放弃选择的机会;

单选按钮控件的测试

测试方法:

a,一组单选按钮不能同时选中,只能选中一个。
b,逐一执行每个单选按钮的功能。分别选择了“男”“女”后,保存到数据库的数据应该相应的分别为“男”“女”;
c,一组执行同一功能的单选按钮在初始状态时必须有一个被默认选中,不能同时为空;

up-down控件文本框的测试

测试方法:

a,直接输入数字或用上下箭头控制,如,在“数目”中直接输入10,或者单击向上的箭头,使数目变为10;
b,利用上下箭头控制数字的自动循环,如,当最多数字为253时,单击向上箭头,数目自动变为1;反之亦适用;
c,直接输入超边界值,系统应该提示重新输入;
d,输入默认值,空白。如,“插入”数目为默认值,点击“确定”;或,删除默认值,使内容为空,单击“确定”进行测试;
e,输入字符。此时系统应提示输入有误。

组合列表框的测试

测试方法:

a,条目内容正确,其详细条目内容可以根据需求说明确定;
b,逐一执行列表框中每个条目的功能;
c,检查能否向组合列表框输入数据;

复选框的测试

测试方法:

a,多个复选框可以被同时选中;
b,多个复选框可以被部分选中;
c,多个复选框可以都不被选中;
d,逐一执行每个复选框的功能;

列表框控件的测试

测试方法:

a,条目内容正确;同组合列表框类似,根据需求说明书确定列表的各项内容正确,没有丢失或错误;
b,列表框的内容较多时要使用滚动条;
c,列表框允许多选时,要分别检查shift选中条目,按ctrl选中条目和直接用鼠标选中多项条目的情况;

滚动条控件的测试

要注意一下几点:

a,滚动条的长度根据显示信息的长度或宽度及时变换,这样有利于用户了解显示信息的位置和百分比,如,word中浏览100页文档,浏览到50页时,滚动条位置应处于中间;
b,拖动滚动条,检查屏幕刷新情况,并查看是否有乱码;
c,单击滚动条;
d,用滚轮控制滚动条;
e,滚动条的上下按钮。

各种控件在窗体中混和使用时的测试

a,控件间的相互作用;
b,tab键的顺序,一般是从上到下,从左到右;
c,热键的使用,逐一测试;
d,enter键和esc键的使用;

在测试中,应遵循由简入繁的原则,先进行单个控件功能的测试,确保实现无误后,再进行多个控件的的功能组合的测试。

ps:密码输入框测试时要特别注意进行字母大写输入的测试。

查找替换操作
案例演示:打开word中的”替换”对话框
测试本功能有通过测试和失败测试两种情况
通过测试:

1,输入内容直接查找,或查找全部
2,在组合框中寻找已经查找过的内容,再次查找并确认文档的内容正确,如,已经查找过”测试用例”,再次进入不用重新输入查找内容,直接在文档中搜寻就可以.

失败测试:
1,输入过长或过短的查询字符串.如,假设查询的字符串长度为1到255,那么输入0,1,2,256,255和254进行测试;
2,输入特殊字符集,如,在word中.^g代表图片,^代表分栏符,可以输入这类特殊字符测试;

替换测试大体相同.
关于编辑操作窗口的功能测试的用例:
1,关闭查找替换窗口.不执行任何操作,直接退出;
2,附件和选项测试.假如,设定”精确搜寻”,”向后”搜索等附件选项等等来测试;
3,控件间的相互作用.如,搜寻内容为空时,按钮”搜寻全部”,”搜寻”,”全部替换”,”替换”都为灰色.
4,热键, Tab键.回车键的使用.

插入操作
1,插入文件
测试的情况
a,插入文件;
b,插入图像;
c,在文档中插入文档本身;
d,移除插入的源文件;
e,更换插入的源文件的内容;

2,链接文件
测试方法:
a,插入链接文件;
b,在文档中链接文档本身;
c,移除插入的源文件;
d,更换插入的源文件的内容.

3,插入对象
要测试的内容
a,插入程序允许的对象,如,在word中插入excel工作表;
b,修改所插入对象的内容.插入的对象仍能正确显示;
c,卸载生成插入对象的程序,如,在word中插入excel工作表后卸载excel,工作表仍正常使用.

编辑操作
编辑操作包括剪切,复制,粘贴操作.

测试剪切操作的方法
a,对文本,文本框,图文框进行剪切;
b,剪切图像
c,文本图像混合剪切
复制操作方法与剪切类似.

测试时,主要是对粘贴操作的测试,方法是:
a,粘贴剪切的文本,文本框及图文框;
b,粘贴所剪切的图像;
c,剪切后,在不同的程序中粘贴
d,多次粘贴同一内容,如,剪切后,在程序中连续粘贴3次;
e,利用粘贴操作强制输入程序所不允许输入的数据.

界面测试用例的设计方法
1,窗体
测试窗体的方法:
a,窗体大小,大小要合适,控件布局合理;
b,移动窗体.快速或慢速移动窗体,背景及窗体本身刷新必须正确;
c,缩放窗体,窗体上的控件应随窗体的大小变化而变化;
d,显示分辨率.必须在不同的分辨率的情况下测试程序的显示是否正常;
进行测试时还要注意状态栏是否显示正确;工具栏的图标执行操作是否有效,是否与菜单懒中图标显示一致;错误信息内容是否正确,无错别字,且明确等等;

2,控件
测试方法:
a,窗体或控件的字体和大小要一致;
b,注意全角,半角混合
c,无中英文混合.

菜单

进行测试时要注意
a,选择菜单是否可以正常工作,并与实际执行内容一致;
b,是否有错别字:
c,快捷键是否重复;
d,热键是否重复;
e,快捷键与热键操作是否有效
f,是否存在中英文混合
g,菜单要与语境相关,如,不同权限的用户登陆一个应用程序,不同级别的用户可以看到不同级别的菜单并使用不同级别的功能;
h,鼠标右键快捷菜单

特殊属性
1,安装界面应有公司介绍或产品介绍,有公司的图标
2,主界面及大多数界面最好有公司图标
3,选择”帮助”->”关于”命令,应看见相关版权和产品信息

web安全测试的checklist

1. 不登录系统,直接输入登录后的页面的url是否可以访问
2. 不登录系统,直接输入下载文件的url是否可以下载,如输入http://url/download?name=file是否可以下载文件file
3. 退出登录后按后退按钮能否访问之前的页面
4. ID/密码验证方式中能否使用简单密码。如密码标准为6位以上,字母和数字混合,不能包含ID,连续的字母或数字不能超过n位
5. 重要信息(如密码,身份证号码,信用卡号等)在输入或查询时是否用明文显示;在浏览器地址栏里输入命令javascrīpt:alert(doucument.cookie)时是否有重要信息;在html源码中能否看到重要信息
6. 手动更改URL中的参数值能否访问没有权限访问的页面。如普通用户对应的url中的参数为l=e,高级用户对应的url中的参数为l=s,以普通用户的身份登录系统后将url中的参数e改为s来访问本没有权限访问的页面
7. url里不可修改的参数是否可以被修改, 浏览器缓存:认证和会话数据不应该作为GET的一部分来发送,应该使用POST
8. 上传与服务器端语言(jsp、asp、php)一样扩展名的文件或exe等可执行文件后,确认在服务器端是否可直接运行
9. 注册用户时是否可以以’–,’ or 1=1 –等做为用户名
10. 传送给服务器的参数(如查询关键字、url中的参数等)中包含特殊字符(’,’and 1=1 –,’ and 1=0 –,’or 1=0 –)时是否可以正常处理

11. 执行新增操作时,在所有的输入框中输入脚本标签(<scrīpt>alert(“”)</scrīpt>)后能否保存

12. 在url中输入下面的地址是否可以下载:
http://url/download.jspfile=C:\windows\system32\drivers\etc\hosts,http://url/download.jsp?file=/etc/passwd
13. 是否对session的有效期进行处理
14. 错误信息中是否含有sql语句、sql错误信息以及web服务器的绝对路径等

15. ID/密码验证方式中,同一个账号在不同的机器上不能同时登录

16. ID/密码验证方式中,连续数次输入错误密码后该账户是否被锁定
17. 新增或修改重要信息(密码、身份证号码、信用卡号等)时是否有自动完成功能(在form标签中使用autocomplete=off来关闭自动完成功能
18.Email Header Injection(邮件标头注入)
Email Header Injection:如果表单用于发送email,表单中可能包括“subject”输入项(邮件标题),我们要验证subject中应能escape掉“\n”标识。
<!–[if !supportLists]–> <!–[endif]–>因为“\n”是新行,如果在subject中输入
“hello\ncc:spamvictim@example.com”,可能会形成以下
Subject: hello
cc: spamvictim@example.com
<!–[if !supportLists]–> <!–[endif]–>如果允许用户使用这样的subject,那他可能会给利用这个缺陷通过我们的平台给其它用户发送垃圾邮件。

19. Directory Traversal(目录遍历)

(1)如何进行目录遍历测试?
目录遍历产生的原因是:程序中没有过滤用户输入的“../”和“./”之类的目录跳转符,导致恶意用户可以通过提交目录跳转来遍历服务器上的任意文件。
测试方法:在URL中输入一定数量的“../”和“./”,验证系统是否ESCAPE掉了这些目录跳转符。
(2)如何预防目录遍历?
限制Web应用在服务器上的运行  进行严格的输入验证,控制用户输入非法路径
20. exposed error messages(错误信息)

(1)如何进行测试?

 

首先找到一些错误页面,比如404,或500页面。
验证在调试未开通过的情况下,是否给出了友好的错误提示信息比如“你访问的页面不存在”等,而并非曝露一些程序代码。
(2)如何预防?
测试人员在进行需求检查时,应该对出错信息进行详细查,比如是否给出了出错信息,是否给出了正确的出错信息。

Android学习第一天-adb常用命令

1.Android Debug Bridge

1.1简介

Android Debug Bridge,我们一般简称为adb,主要存放在sdk安装目录下的platform-tools文件夹中,它是一个非常强大的命令行工具,通过这个工具你能够与你的android设备进行交互。同时这是一个包含了以下三部分的cs模式的程序:

1) adb client

从图中,我们知道client是运行在PC端的,每当我们发起一个adb命令的时候,就会开启一个client程序。当然,当我们开启DDMS或者ADT的时候,也会自动创建client。

当我们开启一个client的时候,它首先会去检测后台是否已经有一个server程序在运行着,否则会开启一个adb-server进程。

所有的client都是通过5037端口与adb-server进行通信的。

2)adb daemon(adbd)

从图中,我们知道daemon是作为一个后台进程运行在模拟器/真实Android设备中的。

daemon使用端口的范围是5554-5585,每个模拟器/设备连接到PC端时,总会开启这么一个后台进程,并且为其分配了两个连续的端口,比如:

Emulator 1, console: 5554

Emulator 1, adb: 5555

也正因为每个设备都分一组两个端口,也已adb连接手机的最大数量为16。

说回端口的作用,在这两个端口中,其中偶数端口是用于server 与设备进行交互的,可以让server直接从设备中读取数据,而奇数端口是用来与设备的adbd进行连接通信的。

2) adb server

从图中,我们同样可以知道,server也是作为一个后台的程序运行在PC端的,他负责管理client进程以及adb daemon之间的通信。

当一个server开启的时候,他会自动绑定并且监听5037端口,接收client通过该端口发送过来的命令。同时server还会对5555-5585间的奇数端口进行扫描,进行对已连接设备的定位。

 1.2 常用的adb命令

首先我们先来介绍一下,adb命令的使用格式吧:

adb [-d|-e|-s <serialNumber>] <command>

adb是必须的,接下来方括号[]里面的内容不是必须的,最后才是我们需要执行的命令操作,例如 adb -s emulator-5554 install UCBrowser.apk (这是安装UC浏览器的意思)

下面先介绍下,前面几个参数的含义吧:

-d: 让唯一连接到该PC端的真实安卓设备执行命令,如果发现USB中连接有多部设备,将会报错

-e: 让唯一连接到该PC端的模拟器执行命令,如果发现开启了多个模拟器,将会报错

-s:通过设备的序列号进行指定设备执行命令

如果设备只连接有一个设备或者一个模拟器的时候,可以不用声明这三个参数,adb默认会让这部唯一连接到的设备进行命令执行。

ok,下面开始讲解常用的命令吧:

1) 查看当前PC端连接有多少设备:

adb devices

终端中的执行结果为:

从上图中,我们看到了设备有的两种状态,其实执行该命令,可能返回的状态有三种:

I. device 设备已经成功连接到了adb-server

II. offline 设备并没有连接到adb或者没有响应

III. no device 并没有设备/模拟器连接

 2) 查看adb的版本

adb version

终端中的执行结果为:

3) 给设备进行软件的安装

adb -s <serialNumber> install <path-to-apk>
eg:
adb -s 99eb07a9 install D://Test.apk

终端中的执行结果为:

除了上面这种方法,我们还有另外一个命令:

adb -s <serialNumber> shell pm install [options] <PATH>

其中[options]中最常用的选项是-r,代表着本次安装是重新安装,会保留用户数据。

4) 卸载设备中已经安装的软件

adb -s <serialNumber> uninstall <pkg_name>
eg:
adb -s 99eb07a9 uninstall cn.uc.test

终端中的执行结果为:

除了上面这种方法,我们还有另外一个命令:

adb -s <serialNumber> uninstall [options] <PACKAGE>

这里的options是-k,加上该参数表明卸载软件后依旧保留用户数据

5) 将数据从设备复制到PC中

adb -s <serialNumber>pull <remote> <local>
eg:
adb -s 99eb07a9 pull /sdcard/stericson-ls D://

终端的执行结果:

在D://盘中我们也可以发现stericson-ls文件

6) 讲数据从PC端复制到设备中

adb -s <serialNumber>push <local> <remote>
eg:
adb -s 99eb07a9 push d://stericson-ls /sdcard/

终端的执行结果:

7) 获取连接到的手机的序列号

adb get-serialno

终端的执行结果:

8)获取设备的连接状态

adb -s <serialNumber> get-state
eg:
adb -s 99eb07a9 get-stat

终端的执行结果:

9) 等待设备连接成功

adb wait-for-device

该命令将会阻塞一直到默认连接的那部机器状态变为device,也就是连接成功。

10) adb-server 的开启与关闭

adb kill-server
adb start-server

终端的执行结果:

有时候我们开启server会失败,这是因为我们装了的手机助手,豌豆荚或者其他软件自带有自己的adb,导致端口冲突开启失败。主要关掉那些软件并且杀掉那些软件自带的adb进程重来即可。

1.2 手机执行Shell命令

众所周知,Android的内核本身就是Linux,所以Android本身也支持Linux命令,但是因为是手机系统,所以进行了一定的删减,部分命令不支持,而不支持的部分,我们可以通过安装busybox这个软件进行

填补。

下面我们讲解下如何在手机中执行Shell命令:

1) 每次都加上adb shell来进行命令的发送与执行

adb shell <command>

2) 先通过adb shell进入手机Shell执行状态,然后直接输入Linux命令,如图:

1.3 Activity Manager 常用命令与参数说明

一般情况下,我们最常用它来进行app的开启,并且传递一定的参数过去,比如:

adb shell am start -a android.intent.action.VIEW -n cn.uc.test/.MainActivity -d http://www.baidu.com

在这里就不做太过详细和重复的说明了

1.4 Package Manager 常用命令与参数说明

1) 使一个apk处于禁用状态,相当于卸载了:

disable <PACKAGE_OR_COMPONENT>
其中 PACKAGE_OR_COMPONENT的格式是 package/class, 如 com.UCMobile/.main.UCMobile
eg:
adb shell su disable com.UCMobile/.main.UCMobile

终端执行结果:

同时我们在手机上发现,UC浏览器的图标不见了

2) 让一个apk从禁用状态恢复过来

enable <PACKAGE_OR_COMPONENT>
其中 PACKAGE_OR_COMPONENT的格式是 package/class, 如 com.UCMobile/.main.UCMobile
eg:
adb shell su enable com.UCMobile/.main.UCMobile

终端执行结果:

同时我们从手机上发现,UC浏览器的图标又回来啦。

3) 查看一个apk所在的路径

pm path <Package>
eg:
adb shell pm path com.UCMobile

终端执行结果:

4) 查看手机中装的所有的包

pm list packages

 

1.5其他常用的adb命令

1) 屏幕录像

例如,屏幕开始录像并且储存到/sdcard中,同时名字为demo.mp4

adb shell screenrecord /sdcard/demo.mp4

2) 关闭设备请求,开启设备

adb shell stop
adb shell start

3)设备关机以及重启

adb reboot
adb shutdown

 

jmeter使用代理录制web脚本

Apache JMeter是一款纯Java的应用程序,用于对软件或系统做性.能测试,如压力测试、负载测试。最初设计是用于web应用测试,由于开源其测试组件不断被扩充,逐步扩展到其他测试领域中。

jmeter制作脚本通常有三种方法,第一种是使用badboy录制jmx脚本,并导入jmeter中,第二种是使用代理录制Web性能测试脚本,最后一种,就是自己写脚本,可以通过fiddler抓包进行修改,制作脚本。下面主要说一下使用代理录制jmeter脚本。

第一步:启动JMeter,双击运行jmeter.bat。

第二步:添加线程组:右击”测试计划”→添加→Threads(Users)→线程组

第三步:添加http代理服务器:右击”工作台”→添加→非测试元件→HTTP代理服务器1504858976(1)

端口写默认的8080,https Domains 写本机IP

目标控制器:下拉选择我们上面建好的线程组

第四步:配置浏览器代理。

–在chrome浏览器中点击右上角设置–高级–打开代理设置–局域网设置–选中”为LAN使用代理服务器”。

–输入地址和端口。地址:localhost或本机的IP;端口:8080。

–最后依次点击”确定”。

第五步:打开浏览器,输入要录的网址,按步骤操作,jmeter线程组就会录下所有的请求。1504860036(1)

 

 

 

 

 

第六步:添加”查看结果树”。

–右击”线程组”→添加→监.听器→查看结构树。

–点击”启动”(Ctrl+R)按钮查看运行结果。

注意一下,如果我们录入时不过滤脚本,那录下来的脚本会有很多无用的,很混乱。所以,每次录入时,先设置好排除没用的请求。点击排除模式的添加按钮:输入要过滤的后缀,用”.*.”加上后缀,如图:

1504860330(1)