Test
关于test:HTTP基准压测工具wrk使用指南
前言wrk是一个开源的、热门的、古代的单机HTTP基准测试工具,目前在github开源平台累计了26.9k的star数目,足以可见wrk在Http基准测试畛域的热门水平。它联合了多线程设计和可扩大的事件告诉零碎,如epoll和kqueue,能够在无限的资源下并收回极致的的负载申请。并且内置了一个可选的LuaJIT脚本执行引擎,能够解决简单的HTTP申请生成、响应解决以及自定义压测报告。wrk我的项目地址:https://github.com/wg/wrk 装置wrkmac下装置: brew install wrk其余平台参考:https://github.com/wg/wrk/wiki 根底应用wrk -t12 -c100 -d30s --latency http://localhost:8010/healthz如上指令形容了采纳12个线程,100个链接,针对/healthz 接口服务,继续压测30s。wrk自身不是依赖线程数来模仿并发数的所以线程数量设置在外围数左右最好,线程数多了测试零碎耗费大,可能带来反成果。亲测外围数统一的线程数和两倍外围数的线程数,前者压出的QPS更高。压测后果如下: Running 30s test @ http://localhost:8010/healthz (运行30s测试) 12 threads and 100 connections(12个线程100个连贯) Thread Stats Avg(均值) Stdev(规范差值) Max(最大值) +/- Stdev(正负规范差值) Latency(提早) 1.39ms 668.10us 23.95ms 90.34% Req/Sec(每秒申请数) 5.44k 545.23 10.27k 76.47% Latency Distribution(提早直方图) 50% 1.32ms (50%申请提早在1.32ms内) 75% 1.49ms (75%申请提早在1.49ms内) 90% 1.72ms (90%申请提早在1.72ms内) 99% 4.77ms (99%申请提早在4.77ms内) 1952790 requests in 30.08s, 271.90MB read (共1952790次申请,用时30s,传输了271.9M数据)Requests/sec(每秒申请数): 64930.12Transfer/sec(每秒传输数据): 9.04MBwrk的后果相比ab测试后果来说,多了一个延时直方图,有了这个直方图,咱们能够更清晰的看到提早的散布状况。这也是博主抉择wrk最重要的起因 罕用指令阐明 -c, --connections: 要放弃关上的HTTP连贯的总数,每个线程解决数N =连贯/线程 -d, --duration: 测试持续时间, 如 2s, 2m, 2h -t, --threads: 测试线程总数 -s, --script: 指定加载lua测试扩大脚本 -H, --header: 增加申请头信息, 如"User-Agent: wrk" --latency: 打印提早直方图信息 --timeout: 如果在此工夫内没有收到响应,则记录超时.-结尾的指令为简写的,前面两个打印提早直方图和超时设置没有简写的,只能--结尾指定 ...
关于test:test
1. Hello World1.1. 根本构造创立 helloWorld.sh 文件,写入如下内容:
关于test:网络协议osi七层协议
1、互联网的实质两台计算机之间的通信和两个人打电话的原理是一样的:1)通过各种物理连贯介质连贯。2)找精确确定对方计算机(精确到软件)的地位。3)通过对立的规范进行数据的收发。 2、OSI七层协定互联网协议依照性能不同分为osi七层、tcp/ip五层、tcp/ip四层。 tcp/ip四层tcp/ip五层osi七层每层运行常见的物理设施应用层应用层应用层 应用层应用层表示层 应用层应用层会话层 传输层传输层传输层四层交换机、四层路由器网络层网络层网络层路由器、三层交换机网络接口层数据链路层数据链路层网桥、以太网交换机、网卡网络接口层物理层物理层中继器、集线器、双绞线咱们将应用层、表示层、会话层并作应用层,从tcp/ip五层协定的角度来论述每层的由来和性能。 物理层物理层性能:次要是基于电器个性发送高下电压(电信号),高电压对应数字1,低电压对应数字0。 数据链路层数据链路层的性能:定义了电信号的分组形式。1)以太网协定:以太网协定(Ethernet)规定:一组电信号形成一个数据包,叫做“帧”。每一数据帧分成:报头head和数据data两局部。 headdatahead蕴含(固定18个字节):发送者/源地址,6个字节;接收者/指标地址,6个字节;数据类型,6个字节data蕴含(最短46字节,最长1500字节):数据包的具体内容head长度+data长度=最短64字节,最长1518字节,超过最大限度就分片发送。2)mac地址head中蕴含的源和目标地址由来:Ethernet规定接入Internet的设施都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址Mac地址:每块网卡出厂时都被烧制上一个世界上惟一的mac地址,长度为48位2进制,通常由12位16进制数示意(前六位是厂商编号,后六位是流水线号)3)播送有了mac地址,同一网络内的两台主机就能够通信了(一台主机通过arp协定获取另一台主机的mac地址)Ethernet采纳最原始的形式,播送的形式进行通信,即计算机通信根本靠吼。 网络层网络层的由来:有了Ethernet、Mac地址、播送的发送形式,世界上的计算机就能够彼此通信了,但世界范畴的互联网是由一个个彼此隔离的小的局域网组成的,那么如果所有的通信都采纳以太网的播送形式,那么一台机器发送的包全世界都会收到,这不仅效率低,更是劫难。 网络层性能:引入一套新的地址来辨别不同的播送域/子网,这套地址即网络地址。 1)IP协定:规定网络地址的协定叫ip协定,它定义的地址称之为ip地址,宽泛采纳的v4版本即ipv4,它规定网络地址由32位2进制示意,范畴0.0.0.0-255.255.255.255 2)Ip地址分成两局部:网络局部:标识子网主机局部:标识主机留神:单纯的ip地址段只是标识了ip地址的品种,从网络局部或者主机局部都无奈辨识一个ip所处的子网。 3)子网掩码:所谓子网掩码,就是标识网络特色的一个参数。它在模式上等同于IP地址,也是一个32位二进制数字,它的网络局部全副为1,主机局部全副为0。 晓得子网掩码,咱们就能判断,任意两个IP地址是否处在同一个子网络。办法就是将两个IP地址与子网掩码别离进行AND运算(两个数位都为1,运算后果为1,否则为0),而后比拟后果是否雷同,雷同则示意在同一个子网络,否则不是。 4)IP数据包:分为head和data局部,毋庸为ip包定义独自的栏位,间接放入以太网包的data局部。head:长度为20到60字节data:最长为65515字节 而以太网数据包的“数据”局部,最长只有1500字节。因而如果ip数据包超过了1500字节,就须要宰割成几个以太网数据包发送。 以太网头ip头ip数据5)ARP协定 Arp协定性能:播送的形式发送数据包,获取指标主机的mac地址首先通过ip地址和子网掩码辨别出本人所处的子网 场景数据包地址同一子网指标主机mac,指标主机ip不同子网网关mac,指标主机ip剖析172.16.10.10/24和172.16.10.11/24处于同一网络(如果不是同一网络,下表中的指标IP改成172.16.10.1,通过arp获取的是网关的mac) 源mac指标mac源ip指标ip数据局部发送端主机发送端macFF:FF:FF:FF:FF:FF172.16.10.10/24172.16.10.11/24数据这个包会以播送的形式在发送端所处的自网内传输,所有主机接管后拆开包,发现指标ip为本人的就响应,返回本人的mac 传输层传输层的由来:网络层的ip帮咱们区分子网,以太网发层的mac帮咱们找到主机,然而大家应用的都是应用程序,你的电脑上可能同时开启多个应用程序。 那么咱们通过ip和mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。 传输层的性能:建设端口到端口的通信。 端口范畴为0-65535,0-1023为零碎占用端口 1)tcp协定 牢靠传输,tcp数据包没有长度限度,实践上能够有限长,然而为了保障网络的效率,通过TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不用再宰割。 以太网头ip头tcp头数据2)Udp协定: 不牢靠传输,“报头”局部一共只有8个字节,总长度不超过65535字节,正好放进一个ip数据包。 以太网头ip头udp头数据应用层应用层的由来:用户应用的都是应用程序,均工作于应用层,互联网是开发的,数据多种多样,必须规定好数据的组织模式。 应用层的性能:规定应用程序的数据格式。 例如,TCP协定能够为各种各样的程序传输数据,比方email、www、FTP等。必须有不同协定规定电子邮件、网页、FTP数据的格局,这些应用程序协定就形成了“应用层”
关于test:网络协议osi七层协议
1、互联网的实质两台计算机之间的通信和两个人打电话的原理是一样的:1)通过各种物理连贯介质连贯。2)找精确确定对方计算机(精确到软件)的地位。3)通过对立的规范进行数据的收发。 2、OSI七层协定互联网协议依照性能不同分为osi七层、tcp/ip五层、tcp/ip四层。 tcp/ip四层tcp/ip五层osi七层每层运行常见的物理设施应用层应用层应用层 应用层应用层表示层 应用层应用层会话层 传输层传输层传输层四层交换机、四层路由器网络层网络层网络层路由器、三层交换机网络接口层数据链路层数据链路层网桥、以太网交换机、网卡网络接口层物理层物理层中继器、集线器、双绞线咱们将应用层、表示层、会话层并作应用层,从tcp/ip五层协定的角度来论述每层的由来和性能。 物理层物理层性能:次要是基于电器个性发送高下电压(电信号),高电压对应数字1,低电压对应数字0。 数据链路层数据链路层的性能:定义了电信号的分组形式。1)以太网协定:以太网协定(Ethernet)规定:一组电信号形成一个数据包,叫做“帧”。每一数据帧分成:报头head和数据data两局部。 headdatahead蕴含(固定18个字节):发送者/源地址,6个字节;接收者/指标地址,6个字节;数据类型,6个字节data蕴含(最短46字节,最长1500字节):数据包的具体内容head长度+data长度=最短64字节,最长1518字节,超过最大限度就分片发送。2)mac地址head中蕴含的源和目标地址由来:Ethernet规定接入Internet的设施都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址Mac地址:每块网卡出厂时都被烧制上一个世界上惟一的mac地址,长度为48位2进制,通常由12位16进制数示意(前六位是厂商编号,后六位是流水线号)3)播送有了mac地址,同一网络内的两台主机就能够通信了(一台主机通过arp协定获取另一台主机的mac地址)Ethernet采纳最原始的形式,播送的形式进行通信,即计算机通信根本靠吼。 网络层网络层的由来:有了Ethernet、Mac地址、播送的发送形式,世界上的计算机就能够彼此通信了,但世界范畴的互联网是由一个个彼此隔离的小的局域网组成的,那么如果所有的通信都采纳以太网的播送形式,那么一台机器发送的包全世界都会收到,这不仅效率低,更是劫难。 网络层性能:引入一套新的地址来辨别不同的播送域/子网,这套地址即网络地址。 1)IP协定:规定网络地址的协定叫ip协定,它定义的地址称之为ip地址,宽泛采纳的v4版本即ipv4,它规定网络地址由32位2进制示意,范畴0.0.0.0-255.255.255.255 2)Ip地址分成两局部:网络局部:标识子网主机局部:标识主机留神:单纯的ip地址段只是标识了ip地址的品种,从网络局部或者主机局部都无奈辨识一个ip所处的子网。 3)子网掩码:所谓子网掩码,就是标识网络特色的一个参数。它在模式上等同于IP地址,也是一个32位二进制数字,它的网络局部全副为1,主机局部全副为0。 晓得子网掩码,咱们就能判断,任意两个IP地址是否处在同一个子网络。办法就是将两个IP地址与子网掩码别离进行AND运算(两个数位都为1,运算后果为1,否则为0),而后比拟后果是否雷同,雷同则示意在同一个子网络,否则不是。 4)IP数据包:分为head和data局部,毋庸为ip包定义独自的栏位,间接放入以太网包的data局部。head:长度为20到60字节data:最长为65515字节 而以太网数据包的“数据”局部,最长只有1500字节。因而如果ip数据包超过了1500字节,就须要宰割成几个以太网数据包发送。 以太网头ip头ip数据5)ARP协定 Arp协定性能:播送的形式发送数据包,获取指标主机的mac地址首先通过ip地址和子网掩码辨别出本人所处的子网 场景数据包地址同一子网指标主机mac,指标主机ip不同子网网关mac,指标主机ip剖析172.16.10.10/24和172.16.10.11/24处于同一网络(如果不是同一网络,下表中的指标IP改成172.16.10.1,通过arp获取的是网关的mac) 源mac指标mac源ip指标ip数据局部发送端主机发送端macFF:FF:FF:FF:FF:FF172.16.10.10/24172.16.10.11/24数据这个包会以播送的形式在发送端所处的自网内传输,所有主机接管后拆开包,发现指标ip为本人的就响应,返回本人的mac 传输层传输层的由来:网络层的ip帮咱们区分子网,以太网发层的mac帮咱们找到主机,然而大家应用的都是应用程序,你的电脑上可能同时开启多个应用程序。 那么咱们通过ip和mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。 传输层的性能:建设端口到端口的通信。 端口范畴为0-65535,0-1023为零碎占用端口 1)tcp协定 牢靠传输,tcp数据包没有长度限度,实践上能够有限长,然而为了保障网络的效率,通过TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不用再宰割。 以太网头ip头tcp头数据2)Udp协定: 不牢靠传输,“报头”局部一共只有8个字节,总长度不超过65535字节,正好放进一个ip数据包。 以太网头ip头udp头数据应用层应用层的由来:用户应用的都是应用程序,均工作于应用层,互联网是开发的,数据多种多样,必须规定好数据的组织模式。 应用层的性能:规定应用程序的数据格式。 例如,TCP协定能够为各种各样的程序传输数据,比方email、www、FTP等。必须有不同协定规定电子邮件、网页、FTP数据的格局,这些应用程序协定就形成了“应用层”
软件测试笔记十三单元测试的介绍和工具选择
前言单元测试通常是作为软件测试中基础的测试类型,用于测试单独的模块是否可以正常工作。它于功能测试不同,它更加关注的是代码内部的逻辑,而非是用户的需求。 单元测试定义前面粗略了介绍了单元测试的定义,详细的解释是:单元测试也称为模块测试或组件测试。在软件开发过程中,检查软件的单个单元或模块是否正常工作,它是由开发人员在开发人员的环境中完成的。 软件测试通常划分为四个层次,每个层次都会去验证软件产品的功能,质量和性能指标。它们是:单元测试,集成测试,系统测试和验收测试。 单元测试的目标隔离代码的每个部分。确保单个模块功能正确。在开发周期的早期,发现代码缺陷。早期介入,以节省测试成本。允许开发人员在以后重构或升级代码。单元测试的优势它在产品开发周期的早期发现问题。因此它降低了测试成本,早发现一个缺陷的成本要比晚发现它的成本低得多。在改变现有功能(回归测试)时,它同时可以保证减少缺陷。它简化了调试过程(测试驱动开发就是基于测试用例来完成功能开发)。调试是在程序中发现并解决妨碍软件正确运行的缺陷的过程。当实现单元测试时,发现测试失败时,只需要调试代码中所做的更改,就可以快速定位到错误。由于更好的编码标准和实践,它给组织提供了更好的代码文档。单元测试的内容单元测试的方法通常单元测试是使用白盒测试的方法 单元测试的类型单元测试可以是手动测试或者是自动化测试 什么时候执行单元测试它作为测试层次的第一层,通常是在集成测试之前完成。 单元测试有谁执行它需要由对于代码内部逻辑熟悉的人执行,通常是开发或者是白盒测试人员。 单元测试有哪些具体的任务首先需要准备单元测试计划: 准备测试计划回顾测试计划修订测试计划定义单元测试计划的基准数据其次是要准备测试用例和脚本: 准备测试环境和测试用例和脚本回顾测试用例和脚本修订测试用例和脚本定义单元测试用例和脚本的基准数据最后是单元测试的执行。 单元测试的工具市面上有很多单元测试的工具,它们可用于协助单元测试。具体的单元测试的工具的选择还是要根据项目的具体情况决定,比如说项目的具体技术栈,是否可以重用已有的单元测试工具等。我们将提供以下几个示例: Junit:Junit的目标是为JVM开发人员开发一个基础测试的测试框架。这包括关注java 8和更高版本,以及支持多种不同风格的测试。 TestNG:TestNG是一个受JUnit和NUnit启发的测试框架,但是它引入了一些新的功能,这些功能使它更加强大和易于使用,例如: 注释。使用各种可用的策略在任意大的线程池中运行测试(所有方法都在自己的线程中,每个测试类一个线程,等等)。测试您的代码是否是多线程安全的。灵活的测试配置。支持数据驱动测试(使用@dataprovider)。参数支持。强大的执行模型(不再是TestSuite)。由各种工具和插件(eclipse、idea、maven等)支持。嵌入BeanShell以获得更大的灵活性。运行时和日志记录的默认JDK函数(无依赖项)。应用服务器测试的依赖方法。.NUnit:它是.net语言的单元测试框架。最初是从JUnit移植而来的,目前的产品版本3已经被完全重写了,它有许多新特性,并且支持各种各样的.NET平台。 xUnit.net:xUnit.net是一个免费的、开源的、面向社区的.net框架单元测试工具xunit.net是由nunit v2的原始发明者编写的,它是用于c、f、vb.net和其他.net语言单元测试的最新技术。xunit.net与resharper、coderash、testdriven.net和xamarin一起工作。它是.NET基金会的一部分,根据他们的行为准则运作。它是根据Apache 2(OSI批准的许可证)授权的。 JMockit: JMockit是开源的单元测试工具jmockit是一个用于开发人员测试的java工具包,包括模拟api和代码覆盖工具。 EMMA:EMMA是一个用于测量和报告java代码覆盖率的开源工具包。EMMA支持大规模的企业软件开发,同时保持单个开发人员的快速工作和迭代团队中的每个开发人员现在都可以免费获得代码覆盖率,最主要的是可以很快的获得覆盖率! PHPUnit:PHPUnit是一个面向程序员的php测试框架。它是单元测试框架的xunit架构的一个实例。 Cantata:Cantata是一个单元和集成测试工具,使开发人员能够在本地和嵌入式目标平台上验证标准兼容或业务关键代码。Cantata通过自动化,帮助加速符合标准的动态测试要求: 测试框架生成测试用例生成测试执行结果诊断和报告生成TestComplete:TestComplete是一个适用于多种应用类型和技术的自动化测试环境,包括(但不限于)Windows、.NET、WPF、Visual C++、Visual Basic、Delphi、C++Builder、Java和Web应用程序和服务。 Mocha:Mocha是一个功能丰富的javascript测试框架,运行在node.js和浏览器中,使异步测试变得简单有趣。MOCA测试连续运行,允许灵活且准确的报告,同时将异常异常映射到正确的测试用例。托管在github上。 Jasmine:Jasmine是一个用于测试JavaScript代码的行为驱动开发框架它不依赖于任何其他JavaScript框架它不需要dom。而且它有一个干净、明显的语法,所以您可以轻松地编写测试。 总结单元测试作为测试的第一层次,被很早引入项目。所以在项目开始前,我们同时需要对单元测试的技术选型和测试案例的计划进行准备。这样才可以在项目研发阶段,同时实现单元测试,以保证功能模块的功能。如果大伙有关于单元测试的一些看法,也请留言区回复和分享。
软件测试笔记十如何做到有效的集成测试
前言软件产品的构成都是非常复杂的,这也就意味着它将含有多个模块,这些模块通过接口进行交互。针对于这些集成模块的测试,我们称之为集成测试。也可以认为它是由单元测试扩展出来的。 集成测试的定义集成测试是测试单元模块之间的连接或数据传输的过程。它又称为I&T(集成与测试)。 它分为大爆炸法、自上而下法、自下而上法和三明治或混合集成法(自上而下和自下而上相结合)。这个过程是通过使用名为stub和Drivers的虚拟程序来执行,其不需要实现软件整个模块,而只是模拟与调用模块的数据通信即可。 它通常是在单元测试之后完成之后执行的。集成测试中涉及的每个模块都应该在集成测试之前进行单元测试。通过在集成测试之前进行单元测试,可以提高执行软件集成测试的信心。 集成测试也需要编写相应的测试计划,从而减少了测试的混乱,并为有效执行集成测试提供了清晰的路径。 集成测试的目标降低风险验证接口的功能和非功能行为是否符合设计和规定建立对接口质量的信心查找缺陷(可能接口本身或组件或系统内)防止集成接口的缺陷在后期测试中发现如何写集成测试用例假设网页程序中有三个模块,如“登录页”、“收件箱”和“删除邮件”。 在编写集成测试用例时,我们不关注单个模块的功能,因为在单元测试期间应该覆盖单个模块,在集成测试阶段我们主要关注模块之间的通信。根据上述假设,我们必须关注“登录页面如何链接到收件箱页面”和“收件箱页面如何链接到删除邮件模块”。 什么是大爆炸式的集成测试它是将所有模块合并一次,并在完成单个模块测试后验证功能。在大爆炸式集成测试中,只有在所有模块都准备好之后,才能集成各个模块。然后他们会去检查它是否表现良好。在这种类型的测试中,可能会出现一些缺点,例如,可能是在很后期发现缺陷。很难定位缺陷是来自于某个模块或者是接口,亦或是集成方面的问题。 什么是自上而下的集成测试 在自上向下的集成测试中,测试是自上而下进行的。首先测试高级模块,然后测试低级模块,最后将低级模块集成到高级模块,以确保系统工作正常。 在这种类型的测试中,如果模块还没有准备好进行集成测试,那么桩程序(stub)将用作临时模块。 什么是自下而上的集成测试 在自下而上的集成测试中,测试是自下而上进行的。首先测试底层模块,然后测试高层模块,最后将高层模块集成到低层,以确保系统按预期工作驱动程序用作集成测试的临时模块。 桩和驱动程序有什么区别桩和驱动程序用于组件级的测试 假设我们在一个应用程序中有两个模块,即“模块1”和“模块2”。开发人员只开发了应用程序的“模块1”。在他们完成“模块2”的开发之前,我们(测试人员)收到了测试“模块1”的要求。我们可以测试“模块1”,如果其与“模块2”没有依赖关系。假设“模块1”依赖于“模块2”。那我们该怎么办?在这种情况下如果想要测试“模块1”,需要开发人员创建一个桩(stub)模块来替换“模块2”。如果“模块2”依赖于“模块1”,但“模块1”尚未就绪,则采用相同的方法在本例中,我们使用驱动(driver)替换“模块1”。 像我们之前提过的登录并且登录邮箱页面的案例。您必须测试登录页面(假设,邮箱页面正在开发中)。登录页面将在登录后调用邮箱页面,但邮箱页面尚未就绪。为了克服这种情况,开发人员编写了一个虚拟程序作为邮箱页面。这个就是桩(stub)程序。 桩(stub)被称为“程序”。如果“调用的程序”不完整,则将其替换为桩。(这是自上而下的方法)。 再来说说驱动程序,登录页面已经准备好了,但不是邮箱页面。这次假设邮箱页面已经准备好测试,但是登录页面还没有准备好。为了克服这种情况,开发人员编写了一个类似于登录页面的虚拟程序。这个虚拟程序就是驱动程序,驱动程序也就是“调用程序”。如果“调用程序”不完整,则将其替换为驱动程序。(这种情况在自下而上的方法中发生)。 桩驱动自顶向下集成测试中使用桩自下而上集成测试中使用驱动程序在开发子程序时使用桩开发过程中使用驱动程序首先测试最上层的模块最底层模块最先测试它用来模拟未集成的底层模块的行为它用来模拟未集成的上层模块的行为桩是被调用程序驱动是调用程序什么是混合集成测试混合集成测试也称为三明治集成测试。它是自顶向下和自下而上集成测试的结合。 集成测试的工具一些集成测试工具如下: Citrus Integration TestingVectorCAST/C++FitNesseValidata总结集成测试是测试环节中很重要的一个部分,尤其是在今天软件产品的架构都在向微服务的架构转变,那么集成各个服务间的测试就显得尤为重要。希望对大家有所帮助,如果有疑问或者想法,也请大家留言区回复。
使用模板
@@大快朵颐和卡的很看返回了方结合说了话好了老人家哦老杰弗里斯好了
软件测试笔记九怎么样写才是一个好的缺陷报告
前言作为一个开发人员,你是否会因为测试人员提交的缺陷报告缺少重要的内容而无法重现缺陷。作为一个测试人员,你是否会收到开发人员拒绝的缺陷报告,而显示的内容是“它是不可复制的”。这些都是在项目开发中经常会遇到的情况,通常的原因是因为缺陷报告的说明不够完整,没有足够多的有用信息。 举例本人曾经在测试中遇到过这样的一个问题,当我在使用Chrome进行测试的时候,发现在某一个特殊版本,产品所使用的Threejs的库无法正常使用。创建缺陷报告的时候,我列举了所有重现的步骤,但是没有列出具体使用的浏览器的类型和版本。 当开发人员试图重现这个缺陷的时候,他使用的是Firefox。而对应的功能在Firefox中可以正常使用,开发人员直接拒绝修复此缺陷,当我得到打回来的缺陷报告,我又重复了一下缺陷发现还是可以重现,结果又指给了开发人员。。。这样反复操作后,最后通过当面演示才把缺陷给开发人员展示清楚。这样的反反复复的过程,大量的浪费了开发和测试人员的时间和精力。 其根本原因是没有在缺陷报告中提到浏览器的类型和版本,如果测试人员在测试报告中忘记提到重现错误的关键信息,那将面临同样的后果。 有句老话:“你永远不会有第二次机会给人留下第一印象。” 编写好的缺陷报告是每个测试人员都应该具备的技能。您必须向开发团队提供所有必要的详细信息,以解决您的问题。 你想在不被拒绝的情况下修复提交的缺陷吗?所以你必须用一个好的缺陷报告来报告它。 如何写出一个好的缺陷报告?首先,先说下好的缺陷报告的构成要素:缺陷ID、报告者名称、缺陷报告日期、检测者、检测方式、项目名称、发布/构建版本、缺陷/增强、环境、优先级、严重性、状态、描述、复制步骤、URL、预期结果、实际结果、必要的截图和录像。 之前在《缺陷报告应该涵盖哪些内容》提到了这里列举的每一个要素的详细解释。 在正式提交缺陷报告前,一定要重要的事情重现三遍的原则,首先保证缺陷可以被重复三遍。 当确定缺陷存在后,那么确定是否同一个缺陷是否其他的测试人员也已经提交了。通常可以使用一些与缺陷相关的关键字,并在缺陷跟踪工具中搜索如果您没有发现与您发现的错误相同的问题,如果还不确信可以找相关的资深测试咨询,接着您可以开始编写错误报告。 等等,也许我们可以做的更多! 我们是否可以确定相关模块中是否存在相同的问题?如果您发现相关模块中存在相同的问题,则可以在相同的错误报告中解决这些问题。这样就可以节省编写多个缺陷报告的时间,提高测试效率。 接下来我们可以通过在上面提到的构成要素开始编写bug报告,并编写详细的步骤来重现。 在报告一个bug之前,做一个检查表并确保你已经通过了所有的要点。 缺陷重复2-3次。使用一些与缺陷相关的关键字,并在缺陷跟踪工具中搜索。测试类似模块,看看是否有相同的问题。立即报告缺陷。写下详细的步骤来重现错误。写一份好的缺陷总结在写错误报告的过程中注意你的语言,对事不对人,尽量抽取有用的信息。建议使用适当的屏幕截图来说明问题。在发布你的错误报告之前要校对两到三次。总结好的缺陷报告可以让阅读的人员快速准确的定位问题,同样也可以提高整个产品研发的效率。如果大家还有其他好的建议关于缺陷报告,也请在留言区回复我,谢谢。
软件测试笔记二软件开发测试BUG的生命周期
生命周期无论是产品的开发,软件的测试,还是BUG都会有属于自己的生命周期,了解了这些生命周期和它们之间的内在联系,可以让我们更好的理解软件,测试和缺陷管理,同时可以帮助梳理我们平时工作中的一些任务和其在不同生命周期的定位。 软件的开发生命周期(SDLC)什么是软件开发的生命周期?软件项目中遵循的流程,以系统的方式开发产品并交付高质量的产品。通过遵循正确的软件开发流程,软件公司可以很好地应对市场压力并发布高质量的软件。 从需求阶段到部署和维护阶段,每个阶段都会产生生命周期下一个阶段所需的可交付成果。需求被转化为设计。根据设计生成代码。应根据要求对已开发的产品进行测试。测试完成后应立即进行部署。它的目的是创建一种高质量的系统,该系统可以满足或超出客户的期望,可以在当前和计划中的信息技术基础架构中有效且高效地工作,维护成本低廉,并且可以提高成本效益。下面就是一个标准的软件开发的生命周期图。 软件开发的生命周期的意义?可能有同学会有疑问了,即使没有这样一整套完整的软件开发的生命周期,我们也可以同样开发产品? 其原因有下面几点: 它给产品的相关利益者提供了项目计划的可见性同样可以帮助我们避免项目中会出现的一些风险同第一点类似,它使我们能够跟踪和控制项目因为每一个过程都会有可交付成果,所以我们可以保证每一个环节都是正确的,最终保证产品的顺利交付软件开发的生命周期的每一个环节需求收集阶段需求收集和分析是软件开发生命周期中最重要的阶段。业务分析师根据客户的业务需求从客户/客户那里收集需求,并按照规范记录需求(文档名称因公司的定义而有所差异。例如,客户需求规范文档(CRS),业务规范文档(BS),等,并提供给开发团队。需求定义阶段需求收集和分析完成后,下一步就是定义和记录产品需求,并获和客户沟通并且得到认可。这是通过SRS(软件需求规范文档)完成的。 文档中会包括在项目生命周期中要设计和开发的所有产品要求。此阶段涉及的关键人员是项目经理,业务分析师和团队的核心成员。此阶段的结果是软件需求规范。产品设计阶段 它有两层设计。第一层是产品的框架设计,它提供了要开发的软件产品的体系结构,由架构师和高级开发人员完成。第二层是定义产品中的每个功能应如何工作以及每个组件应如何工作,通常是接口设计。这些设计文档将作为下一个环节的输入。产品研发阶段 此阶段涉及所有开发人员。这是我们开始构建软件并开始为产品编写代码的阶段。此阶段的结果是源代码文档(SCD)和开发的产品。产品测试阶段 软件准备完成后,将其发送到测试部门,由测试团队针对各种缺陷对其进行全面测试。他们要么手动测试软件,要么使用自动测试工具,取决于STLC(软件测试生命周期)中定义的各个过程,并确保软件的每个组件都能正常运行,并且符合产品的需求文档。质量保证一旦确定软件没有错误,就进入下一阶段部署。这个阶段测试会对产品有个完整的质量评估。产品研发阶段 在成功测试之后,该产品将交付/部署给客户。部署由部署/实施工程师完成。一旦客户开始使用开发的系统,实际问题就会浮出水面,需要不时解决。解决客户发现的问题是在维护阶段。无法进行100%的测试-因为测试人员测试产品的方式与客户使用产品的方式不同。针对于这些问题,产品研发团队会通过补丁包或者更新版本来解决客户遇到的问题。软件开发生命周期模型的类型:软件开发的生命周期的每一个环节和活动是固定的,针对于这些环节我们有不同的软件开发生命周期模型。比如说经典的瀑布模型和比较火热的敏捷开发流程,下面简单聊一下这两个模型。 瀑布模型瀑布模型是传统和经典的模型。这是一种顺序设计过程,通常在SDLC中使用,在该过程中,进度被视为像瀑布一样自上而下的流动,经过需求收集,可行性研究/分析,设计,编码,测试,安装和维护等不同阶段。每个下一个阶段仅在完成上一个阶段的目标后才开始,并且相对独立。在与进度或成本相比质量更重要的项目中,首选此方法。这种方法最适合要求不变的短期项目。敏捷模型敏捷开发方法是非常流行开发方法之一。当然还有一些其他的敏捷开发方法,但广泛使用的常用方法是Scrum方法。Scrum方法是增量模型和迭代模型的组合,通常适用于项目需求经常变化的项目,和互联网项目。什么是软件测试生命周期(STLC)软件测试生命周期(STLC)确定要执行的测试活动以及何时可以完成这些测试活动。尽管不同组织之间的测试有所不同,但都会存在一个测试生命周期。具体可以分为下面几个过程: 需求分析阶段此阶段的进行依据是BRS(业务需求规范)文档。在此阶段,测试团队从测试的角度研究和分析产品的需求。此阶段有助于确定需求是否可测试。如果任何需求是不可测试的,测试团队可以在这个阶段与不同的部门(客户、业务分析师、技术主管、系统架构师等)进行沟通,以便可以规划缓解策略。可交付成果:所有可测试需求清单、自动化可行性报告,等 测试计划阶段这是真正测试过程的第一步。在这个阶段,测试经理/测试负责人通常需要确定整个项目的测试的工作量和成本估算。根据需求分析制定测试计划。在此阶段进行的活动,如资源统筹和规划、确定不同的测试角色和职责、工具选择(如果需要自动化)、测试的培训等。可交付成果:测试策略、测试计划和测试工作量估算文档。 测试设计阶段 测试团队准备测试用例、测试脚本(如果需要自动化)和测试数据。一旦测试用例准备好,那么这些测试用例将由团队成员或团队领导进行设计用例评审。另外,测试团队准备需求跟踪矩阵(RTM)来保证测试用例可以覆盖需求并且验证是否满足需求。可交付成果:测试用例、测试脚本(如果需要自动化)、测试数据。 测试环境搭建阶段此阶段可以与测试设计阶段并行启动。根据硬件和软件需求表建立测试环境。在有些案例测试团队可能不参与这个阶段,开发团队或者由客户提供测试环境,当然取决于不同产品的性质和特点来定。同时,测试小组应准备冒烟测试用例,以检查给定测试环境的是否符合标准。可交付成果:可用的测试环境。冒烟测试结果符合预期。 测试环境搭建阶段测试团队开始基于计划的测试用例执行测试用例。如果测试用例的结果是通过/失败,那么应该在测试用例中记录相应的结果。为失败的测试用例准备缺陷报告,并应通过bug跟踪工具(如Jira)向开发团队报告缺陷。缺陷修复后将重新测试。可交付成果:测试用例执行报告、缺陷报告。 测试结果分析和产品评价阶段 在此阶段我们将会准备测试最终报告,测试结果矩阵。测试团队召开会议,根据测试覆盖率、质量、时间、成本、软件、业务目标评估测试完成度。测试团队分析测试结果(如测试用例、缺陷报告等),以确定将来必须实施的策略,这将有助于消除即将到来的项目中的流程瓶颈。将根据上述标准编制测试度量和测试结束报告。可交付成果:测试最终报告、测试结果矩阵 什么是产品缺陷的生命周期(BLC,DLC)缺陷生命周期是在软件开发过程中,bug也会有一个完整的生命周期。Bug应该经过什么样的生命周期才能关闭。 Bug生命周期的变化取决于所使用的工具(qc、jira等)和组织中遵循的缺陷管理过程。 什么是一个BUG?软件缺陷可以定义为软件的异常行为。怎么样定义异常行为,它可能是不符合我们的需求文档,软件本身的异常,比如产品的崩溃,性能差等问题。缺陷的生命周期是从发现缺陷时开始,在确保缺陷不被重现后,在缺陷关闭时结束。 产品缺陷的生命周期的环节 创建缺陷当测试人员发现新的缺陷时。他应该向开发团队提供一份适当的缺陷文档,来帮助重现和修复缺陷。在此状态下,测试人员发布的缺陷状态为“创建”。分配缺陷处于创建状态的缺陷将通常由测试负责人/项目负责人分配给开发团队。一旦分配了缺陷,缺陷的状态就变为“已分配”。确认(打开)缺陷 开发团队开始分析确认缺陷并开始修复工作。解决缺陷 当开发人员进行必要的代码更改并验证更改通过时,缺陷的状态将更改为“已修复”,缺陷将传递给测试团队。待测试缺陷 如果状态为“待测试”,则表示缺陷已修复并已经准备好测试是否已修复。需要开发提供必要的对应的产品版本信息或者是补丁。验证缺陷在开发人员修复错误后,测试人员将重新验证该缺陷。如果在软件中没有检测到缺陷,将更改其状态为“已验证”。关闭缺陷 在验证通过后,那么bug的状态将被更改为“关闭”。重新打开缺陷 如果缺陷在重新测试后,发现并没有修复,那么测试人员需要将缺陷状态更改为“重新打开”,需要再一次经过“修复”。重复缺陷 如果缺陷重复了两或者多次,或者缺陷与缺陷的概念或者深层次的起因相同,开发团队将状态更改为“重复”。延迟修复缺陷 在某些情况下,项目经理/测试或者开发主管可能会将缺陷的状态设置为延迟。如果在发布结束时发现缺陷,并且该错误很小或不重要,通常会被建议到下一个版本中修复。或者客户有新的需求变更,通常也会将缺陷状态更改为“延迟修复”,并将在下一版本中修复。拒绝修复缺陷 如果系统是按照需求文档实现的,而缺陷仅仅是由于一些误解(例如使用了旧的需求或未定义的特性)造成的,那么团队领导或开发人员可以将这些错误标记为“拒绝修复”。当然这里还有一些其他的环节比如说: 无法解决的缺陷:技术无法支持,产品架构设计的缺陷,解决缺陷的成本过高。无法重现的缺陷:测试环境不匹配、错误的缺陷文档、数据的不匹配、软件版本不匹配,等原因引起的缺陷需要更多信息的缺陷:如果开发人员无法按照测试人员提供的步骤来重现缺陷,那么开发人员可以将状态更改为“需要更多信息”。在这种情况下,测试人员需要添加详细的重现步骤,并将bug再次分配给开发团队进行修复。如果测试人员编写了一个好的缺陷文档,通常就不会发生这种情况。总结这里大概的介绍了产品研发,软件测试和缺陷的生命周期。这里有助于我们理解测试在整个产品研发生命周期的定位,同时也可以帮助理解测试生命周期的每一个环节,来定义我们每一测试环节的需要和输出,帮助我们更好的进行测试,来提高产品的质量。
前端自动化测试一
目前开发大型应用,测试是一个非常重要的环节,但是大多数前端开发者对测试相关的知识是比较缺乏的。因为可能项目开发周期短根本没有机会写,所以你没有办法体会到前端自动化测试的重要性。 来说说为什么前端自动化测试如此重要! 先看看前端常见的问题: 修改某个模块功能时,其它模块也受影响,很难快速定位bug多人开发代码越来越难以维护不方便迭代,代码无法重构代码质量差增加自动化测试后: 我们为核心功能编写测试后可以保障项目的可靠性强迫开发者编写更容易被测试的代码,提高代码质量编写的测试有文档的作用,方便维护1.测试简介1.1 黑盒测试和白盒测试黑盒测试一般也被称为功能测试,黑盒测试要求测试人员将程序看作一个整体,不考虑其内部结构和特性,只是按照期望验证程序是否能正常工作白盒测试是基于代码本身的测试,一般指对代码逻辑结构的测试。1.2 测试分类单元测试(Unit Testing) 单元测试是指对程序中最小可测试单元进行的测试,例如测试一个函数、一个模块、一个组件... 集成测试(Integration Testing) 将已测试过的单元测试函数进行组合集成暴露出的高层函数或类的封装,对这些函数或类进行的测试 端到端测试(E2E Testing)打开应用程序模拟输入,检查功能以及界面是否正确 1.3 TDD & BDDTDD是测试驱动开发(Test-Driven Development) TDD的原理是在开发功能代码之前,先编写单元测试用例代码 BDD是行为驱动开发(Behavior-Driven Development) 系统业务专家、开发者、测试人员一起合作,分析软件的需求,然后将这些需求写成一个个的故事。开发者负责填充这些故事的内容,保证程序实现效果与用户需求一致。 小结: TDD是先写测试再开发 (一般都是单元测试,白盒测试);而BDD则是按照用户的行为来开发,再根据用户的行为编写测试用例 (一般都是集成测试,黑盒测试)1.4 测试框架Karma:Karma为前端自动化测试提供了跨浏览器测试的能力,可以在浏览器中执行测试用例Mocha:前端自动化测试框架,需要配合其他库一起使用,像chai、sinon...Jest:Jest 是Facebook推出的一款测试框架,集成了 Mocha,chai,jsdom,sinon等功能。...看到这里Facebook 都在推Jest,你还不学吗? Jest也有一些缺陷就是不能像Karma这样直接跑在浏览器上,它采用的是jsdom,优势是简单、0配置! 后续我们通过Jest来聊聊前端自动化测试。 2.Jest的核心应用在说Jest测试之前,先来看看以前我们是怎样测试的 const parser = (str) =>{ const obj = {}; str.replace(/([^&=]*)=([^&=]*)/g,function(){ obj[arguments[1]] = arguments[2]; }); return obj;}const stringify = (obj) =>{ const arr = []; for(let key in obj){ arr.push(`${key}=${obj[key]}`); } return arr.join('&');}// console.log(parser('name=zf')); // {name:'zf'}// console.log(stringify({name:'zf'})) // name=zf我们每写完一个功能,都先需要手动测试功能是否正常,测试后可能会将测试代码注释起来,这样会产生一系列问题。因为会污染源代码,所有的测试代码和源代码混合在一起。如果删除掉,下次测试还需要重新编写。 ...
httptest-的介绍与使用
在写完接口之后都需要对接口进行测试,在 golang 标准库中提供 httptest 包来辅助测试。 因为接口都是需要 IP 地址或域名来访问,httptest 包中默认定义了服务地址 const DefaultRemoteAddr = "1.2.3.4"重要的方法NewRequest(请求体)NewRequest 方法用来创建一个 http 的请求体。 方法定义: func NewRequest(method, target string, body io.Reader) *http.Requestmethod 参数表示测试的接口的 HTTP 方法。target 参数表示接口定义的路由。body 参数表示请求体。NewRecorder(响应体)方法定义: func NewRecorder() *ResponseRecorderNewRecorder 方法用来创建 http 的响应体。返回的类型是 *httptest.ResponseRecorder ,包含接口返回信息,等价于 http.ResponseWriter。 ResponseRecorder类型定义: type ResponseRecorder struct { // http 响应码. Code int // 头部信息 HeaderMap http.Header // 返回的 Body Body *bytes.Buffer // 是否调用 Flush 方法 Flushed bool}NewServer(http服务)方法定义: func NewServer(handler http.Handler) *ServerNewServer 方法用来创建和启动新的服务。同类的还有 NewTLSServer,用来创建带 SSL 的服务。 ...
做可交互的统计图表这套图形语法不容错过
选好可视化“一图胜千言”,是最直观的数据可视化魅力。以图表来传达和沟通信息,其效率远超枯燥乏味的数据表达。 有需求就有市场。数据可视化崭露头角后,各个厂商出备的产品、解决方案,开发者自研的可视化工具、操作平台都如雨后春笋般冒了出来。 受众不同,个人的选择就会不同;需求不同,特色的选择就会不同。但选择繁多,很多开发者和企业就会头疼:有数据可视化的需求,但工具到底该如何选择? AntV-G2是阿里巴巴2018年推出的开源项目,是一套基于可视化编码的图形语法,具有高度的易用性和扩展性。无需关注繁琐的实现细节,一条语句即可构建出各种各样的可交互统计图表。它具备以下特性: 简单、易用:从数据出发,仅需几行代码就能轻松获得想要的图表展示效果完备的可视化编码:以数据驱动,提供从数据到图形的完整映射强大的扩展能力:任何图表,都可以基于图形语法灵活绘制,满足无限创意作为一个非常全面的图表库,AntV G2库有折线图、柱状图、条形图、雷达图、箱体图、面积图、饼图、热力图、仪表盘… …几乎满足了所有基本的图表类需求。 另外,G2还是一个使用WebGL/canvas技术实现的基础图表库,因此既可以在原生js环境下使用,也可以使用任意的js框架。基于G2封装的组件框架有BizCharts和Viser,所以如果使用angular、react、vue的话可以直接使用其封装的组件,和自行动手封装G2组件是一样的效果。 G2的构成一个可视化框架需要四部分: 数据处理模块,对数据进行加工的模块,包括一些数据处理方法。例如:合并、分组、排序、过滤、计算统计信息等图形映射模块,将数据映射到图形视觉通道的过程。例如:将数据映射成颜色、位置、大小等图形展示模块,决定使用何种图形来展示数据,点、线、面等图形标记辅助信息模块,用于说明视觉通道跟数据的映射关系,例如:坐标轴、图例、辅助文本等 在数据处理模块上,dataSet主要通过state状态管理多个dataview视图,实现多图联动,或者关联视图。dataView则是对应的是每一个数据源,通过connector来接入不同类型的数据,通过tranform进行数据的转换或者过滤。最后输出我们理想的数据,dataSet是与g2分离的,需要用到的时候可以加载;* 在图形映射模块上,度量 Scale,是数据空间到图形空间的转换桥梁,负责原始数据到 [0, 1] 区间数值的相互转换工作,从原始数据到 [0, 1] 区间的转换我们称之为归一化操作。我们可以通过chart.source或者chart.scale('field', defs)来实现列定义,我们可以在这对数据进行起别名,更换显示类型(time,cat类型等); * 辅助信息,就是标记数据,方便理解数据; * 图形展示chart图表是一个大画布,可以有多个view视图,geom则是数据映射的图形标识,就是指的点,线,面,通过对其操作,从而展示图形。 大体步骤如下: G2 经典新生目前AntV-G2已更新到3.4版本。通过这次升级,G2往经典的“图形语法”理论注入了新的生命,为大家带来“交互语法” — 一套简洁高效的交互式可视化解决方案。同时,G2的底层渲染进行了升级,实现 SVG 和 Canvas 自由切换。 简洁灵活的交互语法 G2将经典的图形语法理论扩展为“交互语法”,一方面开放 220+ 种交互事件,支持定制最小粒度的图表元素交互,另一方面封装了各类复杂的、常用的交互场景,使丰富灵活的图表交互仅需一行代码实现。 渲染引擎自由切换 G2的绘图引擎开始支持 SVG 和 Canvas 双引擎,以适应更多业务场景。并在拾取、动画管线、碰撞检测等方面进行了优化,G2的绘图能力变得更自由、更流畅。 两种引擎在不同场景的性能对比 256+58的试炼 通过256 plots计划和58+业务模板计划,来向用户提供更丰富的场景,也由此检验G2图表的数据表达能力。 通过256 plots计划,G2挑战了d3.js、R语言社区等经典图表绘制,检验并刺激了G2框架图形能力的更新。 58+业务模板源自真实的业务,由基础的线、柱、饼图表改造而起,进而辐射到分面、迷你图等更复杂的场景,能更好的帮助用户找到理想的可视化解决方案。 DataV数据可视化AntV-G2功能虽然强大,但对于需要开箱即用、直接适用业务的企业而言,距离可视化还缺少一个成熟的产品。幸运的是,阿里云.DataV数据可视化完美承担了这样的一个角色。DataV只需通过拖拽式的操作,使用数据连接、可视化组件库、行业设计模板库、多终端适配与发布运维于等功能,就能让非专业的人员快速地将数据价值通过视觉来传达。 DataV具有丰富的图表库,并外接有国内两大第三方图表组件库——Echarts和今日的主角:AntV-G2。在强大的图表库支持下,DataV可以制作出丰富多样的可视化页面,随心所欲自由搭配图表来做组合。 本文作者:数据智能小二阅读原文 本文为云栖社区原创内容,未经允许不得转载。
AutoML数据增广
DeepAugment是一个专注于数据扩充的自动化工具。 它利用贝叶斯优化来发现针对您的图像数据集定制的数据增强策略。 DeepAugment的主要优点和特点是: 降低CNN模型的错误率(WRN-28-10显示CIFAR10的错误率降低了60%)通过自动化流程可以节省时间比谷歌之前的解决方案——AutoAugment——快50倍完成的包在PyPI上。你可以通过运行以下命令来在终端上安装它: $ pip install deepaugment你也可以访问项目的自述文件或运行谷歌Colab笔记本教程。要想了解更多关于我是如何构建这个的,请继续阅读! 引言数据是人工智能应用中最关键的部分。没有足够的标记数据常常导致过度拟合,这意味着模型将无法归纳为未发现的示例。这可以通过数据扩充来缓解,数据扩充可以有效地增加网络所看到的数据的数量和多样性。它是通过对原始数据集(如旋转、裁剪、遮挡等)应用转换,人为地生成新数据来实现的。然而,确定哪种增强对手头的数据集最有效并不是一项简单的任务。为了解决这个问题,谷歌去年发布了AutoAugment,它通过使用强化学习发现了给定数据集的优化增强。由于强化学习模块的存在,使用谷歌的AutoAugment需要强大的计算资源。由于获得所需的计算能力代价高昂,因此我开发了一种新的方法——DeepAugment,它使用贝叶斯优化而不是强化学习。 如何获得更好的数据努力改进数据质量通常比努力改进模型获得更高的投资回报。改进数据有三种主要方法:收集更多的数据、合成新数据或扩展现有数据。收集额外的数据并不总是可行的,而且可能很昂贵。GANs所做的数据合成是很有前途的,但也很复杂,可能与实际的例子有所不同。 另一方面,数据扩充简单且影响很大。它适用于大多数数据集,并通过简单的图像转换完成。然而,问题是确定哪种增强技术最适合当前的数据集。发现正确的方法需要耗时的实验。即使经过多次实验,机器学习(ML)工程师仍然可能找不到最佳选择。对于每个图像数据集,有效的增强策略是不同的,一些增强技术甚至可能对模型有害。例如,如果使用MNIST digits数据集,应用旋转会使模型变得更糟,因为在“6”上180度旋转会使它看起来像“9”,而仍然被标记为“6”。另一方面,对卫星图像应用旋转可以显著改善结果,因为无论旋转多少次,从空中拍摄的汽车图像仍然是一辆汽车。 DeepAugment:闪电般迅速的autoMLDeepAugment旨在作为一种快速灵活的autoML数据扩充解决方案。更具体地说,它被设计为AutoAugment的更快和更灵活的替代品。(2018年Cubuk等人的博客)AutoAugment是2018年最令人兴奋的发布之一,也是第一种使用强化学习来解决这一特定问题的方法。在本文发表时,AutoAugment的开源版本没有提供控制器模块,这阻碍了用户为自己的数据集使用它。此外,学习增强策略需要15,000次迭代,这需要巨大的计算资源。即使源代码完全可用,大多数人也无法从中受益。deepaugmented通过以下设计目标来解决这些问题:1.在保证结果质量的前提下,最小化数据扩充优化的计算复杂度。2.模块化和人性化。为了实现第一个目标,与AutoAugment相比,DeepAugment的设计具有以下差异: 使用贝叶斯优化代替强化学习(需要更少的迭代)(~100x加速)最小化子模型大小(降低每次训练的计算复杂度)(~20x加速)减少随机扩充搜索空间设计(减少所需的迭代次数)为了实现第二个目标,即使DeepAugment模块化和人性化,用户界面的设计方式为用户提供了广泛的可能性配置和模型选择(例如,选择子模型或输入自设计的子模型,请参阅配置选项)。 设计扩充策略DeepAugment旨在为给定的图像数据集找到最佳的扩充策略。增强策略被定义为五个子策略的总和,这两个子策略由两种类型的增强技术和两个实值[0,1]组成,决定了每种增强技术的应用能力。我使用imgaug包实现了增强技术,imgaug包以其大量的增强技术(见下文)而闻名。 当多样化和随机应用时,增强是最有效的。例如,与其旋转每个图像,不如旋转图像的某些部分,剪切另一部分,然后对另一部分应用颜色反转。基于这一观察,Deepaugment对图像随机应用五个子策略之一(包括两个增强)。优化过程中,每个图像被五个子策略之一增强的概率(16%)相等,而完全不被增强的概率为20%。虽然这个策略设计受到了autoaugmented的启发,但有一个主要的区别:我没有使用任何参数来应用子策略的概率,以便使策略的随机性更低,并允许在更少的迭代中进行优化。 这个策略设计为贝叶斯优化器创建了一个20维的搜索空间,其中10个维度是分类(增强技术的类型),其他10个维度是实值(大小)。由于涉及到分类值,我将贝叶斯优化器配置为使用随机森林估计器。 DeepAugment如何找到最佳策略DeepAugment的三个主要组件是控制器(贝叶斯优化器),增强器和子模型,整个工作流程如下:控制器采样新的增强策略,增强器按新策略转换图像,子模型是通过增强图像从头开始训练。根据子模型的训练历史计算奖励。奖励返回给控制器,控制器使用此奖励和相关的增强策略更新代理模型(请参阅下面的“贝叶斯优化如何工作”一节)。然后控制器再次采样新策略,并重复相同的步骤。此过程循环,直到达到用户确定的最大迭代次数。控制器(贝叶斯优化器)是使用scikit- optimization库的ask-and-tell方法实现的。它被配置为使用一个随机森林估计器作为其基本估计器,并期望改进作为其获取函数。 贝叶斯优化是如何工作的贝叶斯优化的目的是找到一组最大化目标函数值的参数。 贝叶斯优化的工作循环可以概括为:1.建立目标函数的代理模型2.查找代理上执行得最好的参数3.使用这些参数执行目标函数4.使用这些参数和目标函数的得分更新代理模型5.重复步骤2-4,直到达到最大迭代次数有关贝叶斯优化的更多信息,请阅读高级的这篇解释的博客,或者看一下这篇综述文章。 贝叶斯优化的二维描述,其中x和y轴表示增强类型,点(i,j)处的颜色表示用增强i和j所增强的数据进行训练时CNN模型的精度。 贝叶斯优化的权衡目前用于超参数优化的标准方法有随机搜索、网格搜索、贝叶斯优化、进化算法和强化学习,按方法复杂度排序。在超参数优化的精度、成本和计算时间方面,贝叶斯优化优于网格搜索和随机搜索(参见这里的经验比较)。这是因为贝叶斯优化从先前参数的运行中学习,与网格搜索和随机搜索相反。当贝叶斯优化与强化学习和进化算法进行比较时,它提供了具有竞争力的准确性,同时需要更少的迭代。例如,为了学习好的策略,谷歌的AutoAugment迭代15,000次(这意味着训练子CNN模型15,000次)。另一方面,贝叶斯优化在100-300次迭代中学习良好的策略。贝叶斯优化的经验法则是使迭代次数等于优化参数的次数乘以10。 挑战及对策挑战1:优化增强需要大量的计算资源,因为子模型应该从头开始反复训练。大大减慢了我的工具的开发过程。 尽管使用贝叶斯优化使其更快,但优化过程仍然不够快,无法使开发变得可行。对策:我开发了两种解决方案。首先,我优化了子CNN模型(见下图),这是该过程的计算瓶颈。其次,我以更确定的方式设计了增强策略,使贝叶斯优化器需要更少的迭代。 设计子CNN模型。它在AWS p3.2x大型实例(带有112 TensorFLOPS的Tesla V100 GPU)上以32x32图像在约30秒(120个周期)的时间内完成培训。 挑战2:我在DeepAugment的开发过程中遇到了一个有趣的问题。在通过一遍又一遍地训练子模型来优化增强期间,它们开始过度拟合验证集。当我更改验证集时,我发现的最佳策略表现不佳。这是一个有趣的例子,因为它不同于一般意义上的过度拟合,即模型权重过度拟合数据中的噪声。对策:我没有使用相同的验证集,而是将剩余的数据和训练数据保留为“种子验证集”,并在每次子CNN模型训练时对1000个图像的验证集进行采样(参见下面的数据管道)。这解决了增强过度拟合问题。 如何集成到ML pipeline中DeepAugment发布在PyPI上。你可以通过运行以下命令来在终端安装它: $ pip install deepaugment并且使用方便: from deepaugment.deepaugment import DeepAugmentdeepaug = DeepAugment(my_images, my_labels)best_policies = deepaug.optimize()通过配置DeepAugment,可以获得更高级的用法: from keras.datasets import cifar10# my configurationmy_config = { "model": "basiccnn", "method": "bayesian_optimization", "train_set_size": 2000, "opt_samples": 3, "opt_last_n_epochs": 3, "opt_initial_points": 10, "child_epochs": 50, "child_first_train_epochs": 0, "child_batch_size": 64}(x_train, y_train), (x_test, y_test) = cifar10.load_data()# X_train.shape -> (N, M, M, 3)# y_train.shape -> (N)deepaug = DeepAugment(x_train, y_train, config=my_config)best_policies = deepaug.optimize(300)有关更详细的安装/使用信息,请访问项目的自述文件或运行Google Colab笔记本教程。 ...
10个你应该了解的Git命令(以及Git省时小窍门)
在本文中,我们将讨论那些作为开发人员、数据科学家或产品经理应该知道的各种各样的Git命令。并且将使用Git查看、删除和整理。此外,我们还将介绍如何使用Bash别名和Git编辑器配置转义Vim和节省时间的方法。如果你不熟悉基本的git命令,那么在阅读本文之前,请查看我之前关于git工作流的文章。下面是需要了解的10个命令和它们的一些常见标志。每个命令都链接到该命令的Atlassian Bitbucket指南。查看信息首先,让我们来查看变化。git diff——查看所有本地文件更改。可以附加文件名,以仅显示一个文件的更改。git log——查看所有提交历史记录。也可以用于具有git log -p my_file的文件。输入q退出。git blame my_file——查看谁更改了my_file中的内容和时间。git reflog——显示本地存储库HEAD的更改日志。有助于找到遗失的文件。用git查看信息并不是很混乱。相比之下,Git提供了大量的选项来删除、撤消提交和文件更改。撤消信息git reset、git checkout和git revert用于撤消对存储库所做更改的影响。这些命令可能很难理解。git reset和git checkout可用于提交和单个文件。git revert仅用于提交级别。如果你只是处理尚未合并到协作远程工作中的本地提交,则可以使用这些命令中的任何一个。如果你正在协作工作,并且需要撤销在远程分支中的提交,那么就使用git revert。这些命令中的每一个都可以采用多种选择。 以下是常见用途:git reset –hard HEAD——丢弃自最近提交以来的阶段性和非阶段性更改。指定一个不同的提交,而不是HEAD来丢弃自提交以来的更改。——hard指定阶段性和非阶段性的更改。确保你不会放弃协作者所依赖的远程分支的提交!git checkout my_commit——放弃my_commit之后非阶段性的更改。HEAD通常用于my_commit,以放弃自最近一次提交以来对本地工作目录的更改。checkout最适合用于本地撤销。它不会打乱协作者所依赖的远程分支的提交历史记录!如果你将checkout与分支一起使用,而不是使用提交,则HEAD将切换到指定的分支,并更新工作目录以匹配。这是checkout命令的更常见用法。git revert my_commit——撤消my_commit中更改的效果。当撤消更改时,revert会进行新的提交。revert对于协作项目是安全的,因为它不会覆盖其他用户的分支所可能依赖的历史记录。有时你只想删除本地目录中的未跟踪文件。例如,你可能运行了一些代码,这些代码创建了许多你在repo中不需要的不同类型的文件。那么,你可以在一瞬间把它们清洗干净!git clean -n——删除本地工作目录中的未跟踪文件-n标志用于没有删除任何内容的干运行。使用-f标志来实际删除文件。使用-d标志删除未跟踪的目录。默认情况下,.gitignore未跟踪的文件不会被删除,但是可以更改此行为。现在你已经了解了在Git中撤消操作的工具,那么让我们来看看另外两个命令。整理信息git commit –amend——将阶段性的更改添加到最近的提交。如果没有执行暂存,此命令只允许你编辑最近的提交消息。只有在提交未集成到远程主分支时才使用此命令!git push my_remote –tags——将所有本地标记发送到远程repo。适合于版本控制更改。如果你正在使用python并对构建的包进行更改,bump2version将自动为你创建标记。一旦你推送了标记,就可以在发布中使用它们。这是我制作第一个OSS python包的指南。跟着我,确保你不会错过版本控制的部分!救命,我被困在Vim里出不来了!使用Git,你可能有时会发现自己陷入了Vim编辑器会话。例如,假设你尝试在没有提交消息的情况下提交,Vim将自动打开。如果你不了解Vim,这有点难缠——看看这个在Stack Overflow中超过4,000投票的回答,来了解如何摆脱它。以下是使用保存文件逃避Vim的四步计划:1.按i进入插入模式。2.在第一行输入提交消息。3.按下退出键-——Esc。4.输入:x。别忘了冒号。恭喜,你自由了!改变默认编辑器为了完全避免Vim,可以在Git中更改默认编辑器。这里是一些带有通用编辑器命令的文档。下面是修改我使用的编辑器Atom默认值的命令:假设你已经安装了Atom,现在可以解决其中的Git问题。太棒了!为Git命令创建快捷方式通过在.bash_profile中添加以下别名,为Git命令添加快捷方式。你可以根据自己的喜好调整Git命令的快捷方式。如果你没有.bash_profile,可以使用以下命令在macOS上创建一个:然后打开它:有关.bash_profile的更多信息,请点击这里。现在,当你在终端中输入gs时,它与输入git status相同。请注意,你可以在快捷方式之后在终端中输入其他标志。你也可以制作Git别名,但是那些要求你在快捷命令之前键入git。这就多此一举了。包装在本文中,你已经看到了一些关键的Git命令,并配置了环境以节省时间。现在你已经有了Git和GitHub的基础。准备好下一步了吗?查看这个Bitbucket Git教程,来了解更多。探索Git分支的交互式指南。分支可能不好理解,但绝对值得一看。去玩,去学习,向别人解释这些不同之处。我希望这篇介绍Git和GitHub的文章对你有用。如果可以,请在你最喜欢的社交媒体上分享,这样其他人也能找到它。我写的是如何有效地使用Python、Docker以及其他编程和数据科学工具。如果你对此感兴趣,请在这里阅读更多。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。
再谈自动化测试——我们在编写测试时,应该注意什么
本文首发于泊浮目的专栏:https://segmentfault.com/blog…背景最近项目在测试阶段陆陆续续的测出了一些bug.这个情况刚出现的时候,让笔者很困惑——平时我们的每个feature代码都是跟随着大量看起来考虑很周全的case进入代码仓库的,然而事实还是打了我们的脸.故在本文,笔者将会从最近的所学所想来谈谈编写测试的时候我们应该注意什么.AIR原则与BCDE原则前阵子看了一本书,里面提到了单元测试的一些原则:宏观上,单元测试要符合AIR原则微观上,单元测试的代码层面要符合BCDE原则AIR原则AIR即空气,单元测试亦是如此。当业务代码在线上运行时,可能感觉不到测试用例的存在和价值,但在代码质量的保障上,却是非常关键的。新增代码应该同步增加测试用例,修改代码逻辑时也应该同步保证测试用例成功执行。AIR原则具体包括:A: Automatic (自动化)I: Independent (独立性)R: Repeatable (可重复)简单的解释一下三个原则:单元测试应该是全自动执行的。测试用例通常会被频繁地触发执行,执行过程必须完全自动化才有意义。如果单元测试的输出结果需要人工介入检查,那么它一定是不合格的。单元测试中不允许使用System.out等方法来进行人工验证,而必须使用断言来验证。为了保证单元测试稳定可靠且便于维护,需要保证其独立性。用例之间不允许互相调用,也不允许出现执行次序的先后依赖。BCDE原则编写单元测试用例时,为了保证被测模块的交付质量,需要符合BCDE原则。B: Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。C: Correct,正确的输入,并得到预期的结果。D: Design,与设计文档相结合,来编写单元测试。E: Error,单元测试的目标是证明程序有错,而不是程序无错。为了发现代码中潜在的错误,我们需要在编写测试用例时有一些强制的错误输入(如非法数据、异常流程、非业务允许输入等)来得到预期的错误结果。在ZStack白盒集成测试中实践原则之前提到的原则是基于单元测试的,但在ZStack的白盒测试中也可以作为有价值的参考.戳此了解ZStack的白盒集成测试:https://segmentfault.com/a/11…由于ZStack的整套测试框架也是基于Junit扩展而来,因此也是一定程度上遵循了上面提到的AIR原则.除了A原则,I和R原则在一定程度上打了折扣:I: 如果上一个测试没有清理干净状态,则会影响下一个测试R: 基于上面提到的I,很有可能导致可重复性大打折扣当然,出现这些问题时则表示当前的代码中有bug.但单元测试则不会受到这样的影响——它能测出bug,AIR原则也得以保证.在本次示例中,我们将以VmInstance的创建API即——APICreateVmInstacneMsg作为测试对象.如果读者不是很了解上下文,也可以简单的看一下这个Case:OneVmBasicLifeCycleCaseBorder Test && Error Test边界测试是用来探测和验证代码在处理极端的情况下会发生什么.而错误测试为了保证ZStack在一些错误的状态下做出我们所期待的行为.那么我们该如何编写这样的测试呢?我们先来简单的理一下创建Vm的流程:VmImageSelectBackupStorageFlowVmAllocateHostFlowVmAllocatePrimaryStorageFlowVmAllocateVolumeFlowVmAllocateNicFlowVmInstantiateResourcePreFlowVmCreateOnHypervisorFlowVmInstantiateResourcePostFlow而其中每一个步骤可以分成好几个小步骤,以VmAllocateHostFlow为例:我们可以看到,根据不同的策略,allocateHost里还会有好几个flow.而由于松耦合架构,我们可以在测试中轻易的模拟极端问题的出现,如:找不到合适的BackupStorageHostCapacity的不够Agent返回的回复在某一个时刻与管理节点的状态不同…….以此类推,以上创建vm的8个flow都可以轻易模拟各种边界条件及错误情况.Correct Test && Design Test正确性测试听起来应该会很简单,(比如调用一个API,然后看结果返回是否正确)但如果放到集成测试中,我们还是可以拓展出一些额外的关注点的.还是以上面提到的createVm为例子,我们看到了8个flow,然后里面可能还嵌套着好几个子flow.如图所示:在编写正确性测试时,我们可以考虑额外关注以下几点:APIParam在各个Flow间中转时是否如预期关注管理节点内的服务:Flow之间调用的时序是否符合预期Flow之间流转时,业务目标状态是否符合预期关注管理节点外的服务:对于agent的请求是否符合预期在API调用完后,相关资源的目标状态是否符合预期而与文档结合的测试用例,则应当由团队的测试人员来定义.可以确定的是,这类的测试更加关注于API(即输入输出),而不是内部的状态.
react项目中使用mocha结合chai断言库进行单元测试
react项目中使用mocha结合chai断言库进行单元测试git地址:https://github.com/yancekang/…如果对你有所帮助,感谢start项目搭建create-react-app react-mocha-test 创建一个名称为 react-mocha-test 的react项目进入 react-mocha-test 安装 Mocha 为了操作的方便,请在全面环境也安装一下Mochanpm install -g mocha编写测试脚本1.进入src目录,新建tool.js文件存放我们的需要测试的函数,函数的具体作用这里就不需要解释了。function checkSex (idcard) { if (idcard === undefined || idcard === null) { return ‘男’ } if (parseInt(idcard.substr(16, 1)%2, 10) === 1) { return ‘男’ } else { return ‘女’ }}function add(x, y) { return x + y}function getParameterByName(name, url) { if (!url) url = window.location.href; /*eslint no-useless-escape: / name = name.replace(/[[]]/g, “\$&”); var regex = new RegExp("[?&]" + name + “(=([^&#])|&|#|$)”), results = regex.exec(url); if (!results) return null; if (!results[2]) return ‘’; return decodeURIComponent(results[2].replace(/+/g, " “));}module.exports = { checkSex, add, getParameterByName}2.在根目录中 test 中建立测试脚本文件,列如: index.js在文件中引入我们要测试的函数let {checkSex, add, getParameterByName} = require(’../src/tool/tool.js’)我们还用到了chai断言库,详情请了解 chai所谓"断言”,就是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。使用npm install chai安装通过let expect = require(‘chai’).expect 引入接下来我们就开始写断言测试,这里只写一种测试第一个函数checkSexdescribe(‘根据身份证号码验证用户性别’, function() { it(‘110101199003072615 男’, function() { expect(checkSex(‘110101199003072615’)).to.be.equal(‘男’) }) it(‘110101199003072156 男’, function() { expect(checkSex(‘110101199003072156’)).to.be.equal(‘男’) }) it(‘15010219900307442X 女’, function() { expect(checkSex(‘15010219900307442X’)).to.be.equal(‘女’) }) it(‘150102199003075385 女’, function() { expect(checkSex(‘150102199003075385’)).to.be.equal(‘女’) })})这里举例四种测试用例,根据身份证号码辨别该用户的性别和我们预期的是否一致。基本上,expect断言的写法都是一样的。头部是expect方法,尾部是断言方法,比如equal、a/an、ok、match等。两者之间使用to或to.be连接。如果expect断言不成立,就会抛出一个错误。事实上,只要不抛出错误,测试用例就算通过。在项目根目录执行npm test进行单元测试,可以看到测试结果测试通过测试异常 ...
测试 google 图片能否长久使用
测试 google 图片能否长久使用
EdⅰtText
怎么这么简单
selenium中的window handle
webdriver之window handle实例化一个webdriver后相当于开启一个浏览器进程, 一个实例化的driver可以有多个window窗口,在浏览器中显示为多个标签, 比如点击一个链接 网易,会打开一个新的窗口webdriver类中的所有方法有一个前提条件是:都作用于某一window handlewindow handle是惰性的,不会自动切换,如果打开了一个新的窗口,想在新窗口上获取某一元素,需要先手动切换window handle,driver.switch_to.window(xxx_handle)用driver.window_handles可以获取所有窗口句柄窗口句柄是浏览器拥有的,元素没有窗口句柄window handle示例import time from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains def demo(): driver = webdriver.Chrome() driver.implicitly_wait(10) driver.get('[http://baidu.com](http://baidu.com)') print(driver.window_handles) #打开百度后第一次打印窗口句柄 bd_kw = driver.find_element_by_css_selector('#kw') bd_sb = driver.find_element_by_css_selector('#su') ac = ActionChains(driver) ac.send_keys_to_element(bd_kw, 'python').click(bd_sb).perform() py = driver.find_element_by_xpath('//*[@id="2"]/h3/a') py.click() #在百度中搜索python后打开一个新的窗口 print(driver.window_handles) #第二次打印窗口句柄 time.sleep(5) driver.close() #关闭driver的当前句柄,可以用current_handle查看 print(driver.window_handles) #第三次打印窗口句柄 driver.switch_to.window(driver.window_handles[-1]) #切换window handle print(driver.current_window_handle) #打印current_window_handle,不切换会报异常,因为之前的window已经被我们关闭了 time.sleep(5) driver.quit() if __name__ == '__main__': demo() ---------------------- >>>['CDwindow-3711170FE14EB6A64A8D9A51249D8EF6'] #只打开了百度首页,所以只有一个 >>>['CDwindow-3711170FE14EB6A64A8D9A51249D8EF6', 'CDwindow-1FDDE8A60F9569D82F5A477DCBF6B8E1'] #打开了百度首页和某一个搜索出来的页面,新的页面在新的窗口中,所以有两个 >>>['CDwindow-1FDDE8A60F9569D82F5A477DCBF6B8E1'] #没切换handle,关闭了第一个window,所以看到,原列表中的第一个元素被删除了,只有新的窗口handle保留下来了 >>>CDwindow-1FDDE8A60F9569D82F5A477DCBF6B8E1 #切换了handle,并打印出current_window_handle tips:driver的current handle也是惰性的,如果current window handle被关闭,那么current_handle这个值就取不到了,会报异常,需要用手动调用driver.switch_to.window 来显示切换。如果元素定位失败,要检查一下是不是打开了新的窗口,如果是,则需要切换window handle,因为它不会自动切换。 ...
一份关于kaggle特征构建技巧和心得
摘要: 本文是一份关于如何在Kaggle排行榜上取得出色成绩的提示,包含经纬度数据的处理。在很长的一段时间里,我们表现出缺乏创造力,所做出的工作被认为是山寨、借鉴,这一点是不可否认,但随着自身的积累,厚积薄发,完成了从借鉴到创造的突破。创造力是我们工作的基本要素之一,这点在各行各业都显得很重要,在机器学习领域也无所例外。创建特征也需要创造力,因此本文在这里列出了我日常生活中的一些想法,希望对其它人有些启发,以至于能够在此基础上利用创造力在Kaggle排行榜上取得很好的成绩。这篇文章的灵感来自于 Beluga在Kaggle上分享的文章,本文部分内容是直接摘自该文章中,因此,读者也可以看看这篇文章。以下是分享的正文:1.当不需要时,不要尝试预测未来:如果训练/测试都来自同一时间线,那么就可以非常巧妙地使用特性。虽然这只是一个kaggle的案例,但可以利用这个优势。例如:在出租车出行持续时间挑战赛中,从训练数据中随机抽取测试数据。在这种情况下,可以使用不同类别变量的平均目标变量作为特征。在这种情况下, Beluga 实际上使用了不同工作日的平均目标变量。然后,将相同的平均值映射为一个变量,并将其映射到测试数据中。2. logloss裁剪技术:这部分内容是在Jeremy Howard的神经网络课程中学到的内容,它基于一个非常简单的想法。如果我们非常自信和不公正的,Logloss会受到很多惩罚。因此,在必须预测概率的分类问题情况下,将概率剪切在0.05-0.95之间会好得多,这样就对自己的预测变得不是十分确定。3.以gzip格式提交到kaggle: 下面一小段代码可以帮助我们节省无数的上传时间:df.to_csv(‘submission.csv.gz’, index=False, compression=‘gzip’)4.如何最好地使用纬度和经度特征——第1部分:在Beluga写的文章中,我最喜欢的一部分内容之一就是他如何使用经纬度(Lat / Lon)数据,这里创建了以下特征:A.两个经纬度之间的Haversine距离:def haversine_array(lat1, lng1, lat2, lng2): lat1, lng1, lat2, lng2 = map(np.radians, (lat1, lng1, lat2, lng2)) AVG_EARTH_RADIUS = 6371 # in km lat = lat2 - lat1 lng = lng2 - lng1 d = np.sin(lat * 0.5) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(lng * 0.5) ** 2 h = 2 * AVG_EARTH_RADIUS * np.arcsin(np.sqrt(d)) return hB.两个经纬度之间的曼哈顿距离:def dummy_manhattan_distance(lat1, lng1, lat2, lng2): a = haversine_array(lat1, lng1, lat1, lng2) b = haversine_array(lat1, lng1, lat2, lng1) return a + bC.两个经纬度之间的方位:def bearing_array(lat1, lng1, lat2, lng2): AVG_EARTH_RADIUS = 6371 # in km lng_delta_rad = np.radians(lng2 - lng1) lat1, lng1, lat2, lng2 = map(np.radians, (lat1, lng1, lat2, lng2)) y = np.sin(lng_delta_rad) * np.cos(lat2) x = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(lng_delta_rad) return np.degrees(np.arctan2(y, x))D.取放点之间的中心纬度和经度:train.loc[:, ‘center_latitude’] = (train[‘pickup_latitude’].values + train[‘dropoff_latitude’].values) / 2train.loc[:, ‘center_longitude’] = (train[‘pickup_longitude’].values + train[‘dropoff_longitude’].values) / 25.如何最好地使用经纬度特征——第2部分:在Beluga写的文章中,他使用经纬度数据的第二种方式是为取放点的经纬度创建集群,它的工作方式是通过设计在数据中创建了一些行政区。from sklearn.cluster import MiniBatchKMeanscoords = np.vstack((train[[‘pickup_latitude’, ‘pickup_longitude’]].values, train[[‘dropoff_latitude’, ‘dropoff_longitude’]].values, test[[‘pickup_latitude’, ‘pickup_longitude’]].values, test[[‘dropoff_latitude’, ‘dropoff_longitude’]].values))sample_ind = np.random.permutation(len(coords))[:500000]kmeans = MiniBatchKMeans(n_clusters=100, batch_size=10000).fit(coords[sample_ind])train.loc[:, ‘pickup_cluster’] = kmeans.predict(train[[‘pickup_latitude’, ‘pickup_longitude’]])train.loc[:, ‘dropoff_cluster’] = kmeans.predict(train[[‘dropoff_latitude’, ‘dropoff_longitude’]])test.loc[:, ‘pickup_cluster’] = kmeans.predict(test[[‘pickup_latitude’, ‘pickup_longitude’]])test.loc[:, ‘dropoff_cluster’] = kmeans.predict(test[[‘dropoff_latitude’, ‘dropoff_longitude’]])然后,他使用这些集群创建了一些特征,例如比如计算某一天外出和入境的次数。6.如何最好地使用纬度和经度特征——第3部分在Beluga写的文章中,还使用了PCA方法来转换经度和纬度坐标。在这种情况下,它不是进行降维,而是进行了坐标的变换,2D—>2D变换,它实际上做了如下操作。pca = PCA().fit(coords)train[‘pickup_pca0’] = pca.transform(train[[‘pickup_latitude’, ‘pickup_longitude’]])[:, 0]train[‘pickup_pca1’] = pca.transform(train[[‘pickup_latitude’, ‘pickup_longitude’]])[:, 1]train[‘dropoff_pca0’] = pca.transform(train[[‘dropoff_latitude’, ‘dropoff_longitude’]])[:, 0]train[‘dropoff_pca1’] = pca.transform(train[[‘dropoff_latitude’, ‘dropoff_longitude’]])[:, 1]test[‘pickup_pca0’] = pca.transform(test[[‘pickup_latitude’, ‘pickup_longitude’]])[:, 0]test[‘pickup_pca1’] = pca.transform(test[[‘pickup_latitude’, ‘pickup_longitude’]])[:, 1]test[‘dropoff_pca0’] = pca.transform(test[[‘dropoff_latitude’, ‘dropoff_longitude’]])[:, 0]test[‘dropoff_pca1’] = pca.transform(test[[‘dropoff_latitude’, ‘dropoff_longitude’]])[:, 1]7.不要忘记可以用特征做的正常事情:按Max-Min缩放;使用标准偏差进行标准化;基于特征/目标的日志:使用基于特征或基于目标特征的日志;热编码;8.创建直观的附加特征:A)日期时间特征:基于时间的特征,如“晚上”、“中午”、“夜晚”、“上月购买行为”,“上周购买行为”等;B)思想特征:假设有购物车数据,并且想要对行程进行分类(参阅Walmart Recruiting:Kaggle的行程类型分类);此外,还可以考虑创建一个像“时尚”这样的特征,可以通过添加属于男装时尚、女装时尚、青少年时尚类别的项目来创建这个变量。 另外,也可以创建一个像“稀有”这样的特征,它是根据我们拥有的数据标记一些稀有物品然后计算购物车中稀有物品的数量而创建的,这些特征可能是有效的或无效的。根据我的观察,它们通常能够提供很多价值。9.做的不那么正常的事情:这些特征非常不直观,不应在机器学习模型需要解释的地方创建。A)交互特征:如果有特征A和B,并创建特征A * B、A + B、A / B、AB,这会使得特征空间爆炸。如果你有10个特征,并且要创建两个可变交互特征,这将为模型添加 180个特征。并且,绝大多数时候,都会有超过10个的特征。B)使用散列的存储桶特征:假设你有数千的特征,并按顺序排好,但考虑到算法的训练时间,并不想使用所有的数千千个特征。一般是使用一些哈希算法来实现这一点,最后完成文本分类任务。例如: 假设有6个特征A、B、C、D、E、F:并且数据行是:A:1、B:1、C:1、D:0、E:1、F:0 可能决定使用散列函数,以便这6个特征对应于3个桶并创建使用此特征的数据哈希矢量。处理完后,数据可能如下所示:Bucket1:2、Bucket2:2、Bucket3:0A:1、B:1、C:1、D:0、E:1、F:0 之所以发生这种情况是因为A和B掉落在桶1中、C和E落在桶2中、D和F落在桶3中。这里只是总结了上述的观察结果,你也可以用你想要的任何数学函数替换掉上述的加法操作。之后,将使用Bucket1、Bucket2、Bucket3作为机器学习的变量。A:1、B:1、C:1、D:0、E:1、F:0 以上是本文的全部内容,后续将持续更新,如果读者有比较好的处理方法,请在下面留言给出。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...
Spring、Spring Boot和TestNG测试指南 - 集成测试中用Docker创建数据库
原文地址在测试关系型数据库一篇里我们使用的是H2数据库,这是为了让你免去你去安装/配置一个数据库的工作,能够尽快的了解到集成测试的过程。在文章里也说了:在真实的开发环境中,集成测试用数据库应该和最终的生产数据库保持一致那么很容易就能想到两种解决方案:开发团队使用共用同一个数据库。这样做的问题在于:当有多个集成测试同时在跑时,会产生错误的测试结果。每个人使用自己的数据库。这样做的问题在于让开发人员维护MySQL数据库挺麻烦的。那么做到能否这样呢?测试启动前,创建一个MySQL数据库测试过程中连接到这个数据库测试结束后,删除这个MySQL数据库So, Docker comes to the rescue。我们还是会以测试关系型数据库里的FooRepositoryImpl来做集成测试(代码在这里)。下面来讲解具体步骤:安装Docker请查阅官方文档。并且掌握Docker的基本概念。配置fabric8 docker-maven-pluginfarbic8 docker-maven-plugin顾名思义就是一个能够使用docker的maven plugin。它主要功能有二:创建Docker image启动Docker container我们这里使用启动Docker container的功能。大致配置如下 <plugin> <groupId>io.fabric8</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.28.0</version> <configuration> <images> <image> <!– 使用mysql:8 docker image –> <name>mysql:8</name> <!– 定义docker run mysql:8 时的参数 –> <run> <ports> <!– host port到container port的映射 这里随机选择一个host port,并将值存到property docker-mysql.port里 –> <port>docker-mysql.port:3306</port> </ports> <!– 启动时给的环境变量,参阅文档:https://hub.docker.com/_/mysql –> <env> <MYSQL_ROOT_PASSWORD>123456</MYSQL_ROOT_PASSWORD> <MYSQL_DATABASE>test</MYSQL_DATABASE> <MYSQL_USER>foo</MYSQL_USER> <MYSQL_PASSWORD>bar</MYSQL_PASSWORD> </env> <!– 设置判定container启动成功的的条件及timeout –> <wait> <!– 如果container打出了这行日志,则说明容器启动成功 –> <log>MySQL init process done. Ready for start up.</log> <time>120000</time> </wait> </run> </image> </images> </configuration> <executions> <execution> <!– 在集成测试开始前启动容器 –> <id>start</id> <phase>pre-integration-test</phase> <goals> <goal>start</goal> </goals> </execution> <execution> <!– 在集成测试结束后停止并删除容器 –> <id>stop</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin>配置maven-failsafe-plugin<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <executions> <execution> <id>integration-test</id> <goals> <goal>integration-test</goal> </goals> </execution> <execution> <id>verify</id> <goals> <goal>verify</goal> </goals> </execution> </executions> <configuration> <!– 我们被测的是一个Spring Boot项目,因此可以通过System Properties把MySQL container的相关信息传递给程序 详见文档:https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/html/boot-features-external-config.html –> <systemPropertyVariables> <spring.datasource.url>jdbc:mysql://localhost:${docker-mysql.port}/test</spring.datasource.url> <spring.datasource.username>foo</spring.datasource.username> <spring.datasource.password>bar</spring.datasource.password> </systemPropertyVariables> </configuration></plugin>执行三种常见用法:mvn clean integration-test,会启动docker container、运行集成测试。这个很有用,如果集成测试失败,那么你还可以连接到MySQL数据库查看情况。mvn clean verify,会执行mvn integration-test、删除docker container。mvn clean install,会执mvn verify,并将包安装到本地maven 仓库。下面是mvn clean verify的日志:…[INFO] — docker-maven-plugin:0.28.0:start (start) @ spring-test-examples-rdbs-docker —[INFO] DOCKER> [mysql:8]: Start container f683aadfe8ba[INFO] DOCKER> Pattern ‘MySQL init process done. Ready for start up.’ matched for container f683aadfe8ba[INFO] DOCKER> [mysql:8]: Waited on log out ‘MySQL init process done. Ready for start up.’ 13717 ms[INFO][INFO] — maven-failsafe-plugin:2.22.1:integration-test (integration-test) @ spring-test-examples-rdbs-docker —[INFO][INFO] ——————————————————-[INFO] T E S T S[INFO] ——————————————————-…[INFO][INFO] Results:[INFO][INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0[INFO][INFO][INFO] — docker-maven-plugin:0.28.0:stop (stop) @ spring-test-examples-rdbs-docker —[INFO] DOCKER> [mysql:8]: Stop and removed container f683aadfe8ba after 0 ms[INFO][INFO] — maven-failsafe-plugin:2.22.1:verify (verify) @ spring-test-examples-rdbs-docker —[INFO] ————————————————————————[INFO] BUILD SUCCESS[INFO] ————————————————————————…可以看到fabric8 dmp在集成测试前后start和stop容器的相关日志,且测试成功。如何找到MySQL的端口开在哪一个呢?运行docker ps查看端口(注意下面的0.0.0.0:32798->3306/tcp):CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa1f4b51d7c75 mysql:8 … … Up 19… 33060/tcp, 0.0.0.0:32798->3306/tcp mysql-1参考文档Fabric8 dmpSpring boot - Externalized Configuration ...
机器学习基础:(Python)训练集测试集分割与交叉验证
摘要: 本文讲述了如何用Python对训练集测试集进行分割与交叉验证。在上一篇关于Python中的线性回归的文章之后,我想再写一篇关于训练测试分割和交叉验证的文章。在数据科学和数据分析领域中,这两个概念经常被用作防止或最小化过度拟合的工具。我会解释当使用统计模型时,通常将模型拟合在训练集上,以便对未被训练的数据进行预测。在统计学和机器学习领域中,我们通常把数据分成两个子集:训练数据和测试数据,并且把模型拟合到训练数据上,以便对测试数据进行预测。当做到这一点时,可能会发生两种情况:模型的过度拟合或欠拟合。我们不希望出现这两种情况,因为这会影响模型的可预测性。我们有可能会使用具有较低准确性或不常用的模型(这意味着你不能泛化对其它数据的预测)。什么是模型的过度拟合(Overfitting)和欠拟合(Underfitting)?过度拟合过度拟合意味着模型训练得“太好”了,并且与训练数据集过于接近了。这通常发生在模型过于复杂的情况下,模型在训练数据上非常的准确,但对于未训练数据或者新数据可能会很不准确。因为这种模型不是泛化的,意味着你可以泛化结果,并且不能对其它数据进行任何推断,这大概就是你要做的。基本上,当发生这种情况时,模型学习或描述训练数据中的“噪声”,而不是数据中变量之间的实际关系。这种噪声显然不是任何新数据集的一部分,不能应用于它。欠拟合与过度拟合相反,当模型欠拟合的时候,它意味着模型不适合训练数据,因此会错过数据中的趋势特点。这也意味着该模型不能被泛化到新的数据上。你可能猜到了,这通常是模型非常简单的结果。例如,当我们将线性模型(如线性回归)拟合到非线性的数据时,也会发生这种情况。不言而喻,该模型对训练数据的预测能力差,并且还不能推广到其它的数据上。值得注意的是,欠拟合不像过度拟合那样普遍。然而,我们希望避免数据分析中的这两个问题。你可能会说,我们正在试图找到模型的欠拟合与过度拟合的中间点。像你所看到的,训练测试分割和交叉验证有助于避免过度拟合超过欠拟合。训练测试分割正如我之前所说的,我们使用的数据通常被分成训练数据和测试数据。训练集包含已知的输出,并且模型在该数据上学习,以便以后将其泛化到其它数据上。我们有测试数据集(或子集),为了测试模型在这个子集上的预测。我们将使用Scikit-Learn library,特别是其中的训练测试分割方法。我们将从导入库开始:快速地看一下导入的库:Pandas —将数据文件作为Pandas数据帧加载,并对数据进行分析;在Sklearn中,我导入了数据集模块,因此可以加载一个样本数据集和linear_model,因此可以运行线性回归;在Sklearn的子库model_selection中,我导入了train_test_split,因此可以把它分成训练集和测试集;在Matplotlib中,我导入了pyplot来绘制数据图表;好了,一切都准备就绪,让我们输入糖尿病数据集,将其转换成数据帧并定义列的名称:现在我们可以使用train_test_split函数来进行分割。函数内的test_size=0.2表明了要测试的数据的百分比,通常是80/20或70/30左右。# create training and testing varsX_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2)print X_train.shape, y_train.shapeprint X_test.shape, y_test.shape(353, 10) (353,)(89, 10) (89,)现在我们将在训练数据上拟合模型:# fit a modellm = linear_model.LinearRegression()model = lm.fit(X_train, y_train)predictions = lm.predict(X_test)正如所看到的那样,我们在训练数据上拟合模型并尝试预测测试数据。让我们看一看都预测了什么:predictions[0:5]array([ 205.68012533, 64.58785513, 175.12880278, 169.95993301, 128.92035866])注:因为我在预测之后使用了[0:5],它只显示了前五个预测值。去掉[0:5]的限制就会使它输出我们模型创建的所有预测值。让我们来绘制模型:## The line / modelplt.scatter(y_test, predictions)plt.xlabel(“True Values”)plt.ylabel(“Predictions”)打印准确度得分:print “Score:”, model.score(X_test, y_test)Score: 0.485829586737总结:将数据分割成训练集和测试集,将回归模型拟合到训练数据,基于该数据做出预测,并在测试数据上测试预测结果。但是训练和测试的分离确实有其危险性,如果我们所做的分割不是随机的呢?如果我们数据的一个子集只包含来自某个州的人,或者具有一定收入水平但不包含其它收入水平的员工,或者只有妇女,或者只有某个年龄段的人,那该怎么办呢?这将导致过度拟合,即使我们试图避免,这就是交叉验证要派上用场了。交叉验证在前一段中,我提到了训练测试分割方法中的注意事项。为了避免这种情况,我们可以执行交叉验证。它非常类似于训练测试分割,但是被应用于更多的子集。意思是,我们将数据分割成k个子集,并训练第k-1个子集。我们要做的是,为测试保留最后一个子集。有一组交叉验证方法,我来介绍其中的两个:第一个是K-Folds Cross Validation,第二个是Leave One Out Cross Validation (LOOCV)。K-Folds 交叉验证在K-Folds交叉验证中,我们将数据分割成k个不同的子集。我们使用第k-1个子集来训练数据,并留下最后一个子集作为测试数据。然后,我们对每个子集模型计算平均值,接下来结束模型。之后,我们对测试集进行测试。这里有一个在Sklearn documentation上非常简单的K-Folds例子:fromsklearn.model_selection import KFold # import KFoldX = np.array([[1, 2], [3, 4], [1, 2], [3, 4]]) # create an arrayy = np.array([1, 2, 3, 4]) # Create another arraykf = KFold(n_splits=2) # Define the split - into 2 folds kf.get_n_splits(X) # returns the number of splitting iterations in the cross-validatorprint(kf) KFold(n_splits=2, random_state=None, shuffle=False)让我们看看结果:fortrain_index, test_index in kf.split(X): print(“TRAIN:”, train_index, “TEST:”, test_index)X_train, X_test = X[train_index], X[test_index]y_train, y_test = y[train_index], y[test_index](‘TRAIN:’, array([2, 3]), ‘TEST:’, array([0, 1]))(‘TRAIN:’, array([0, 1]), ‘TEST:’, array([2, 3]))正如看到的,函数将原始数据拆分成不同的数据子集。这是个非常简单的例子,但我认为它把概念解释的相当好。弃一法交叉验证(Leave One Out Cross Validation,LOOCV)这是另一种交叉验证的方法,弃一法交叉验证。可以在Sklearn website上查看。在这种交叉验证中,子集的数量等于我们在数据集中观察到的数量。然后,我们计算所有子集的平均数,并利用平均值建立模型。然后,对最后一个子集测试模型。因为我们会得到大量的训练集(等于样本的数量),因此这种方法的计算成本也相当高,应该在小数据集上使用。如果数据集很大,最好使用其它的方法,比如kfold。让我们看看Sklearn上的另一个例子:fromsklearn.model_selectionimportLeaveOneOutX = np.array([[1, 2], [3, 4]])y = np.array([1, 2])loo = LeaveOneOut()loo.get_n_splits(X)fortrain_index, test_indexinloo.split(X): print(“TRAIN:”, train_index, “TEST:”, test_index)X_train, X_test = X[train_index], X[test_index]y_train, y_test = y[train_index], y[test_index] print(X_train, X_test, y_train, y_test)以下是输出:(‘TRAIN:’, array([1]), ‘TEST:’, array([0]))(array([[3, 4]]), array([[1, 2]]), array([2]), array([1]))(‘TRAIN:’, array([0]), ‘TEST:’, array([1]))(array([[1, 2]]), array([[3, 4]]), array([1]), array([2]))那么,我们应该使用什么方法呢?使用多少子集呢?拥有的子集越多,我们将会由于偏差而减少误差,但会由于方差而增加误差;计算成本也会上升,显然,拥有的子集越多,计算所需的时间就越长,也将需要更多的内存。如果利用数量较少的子集,我们减少了由于方差而产生的误差,但是由于偏差引起的误差会更大。它的计算成本也更低。因此,在大数据集中,通常建议k=3。在更小的数据集中,正如我之前提到的,最好使用弃一法交叉验证。让我们看看以前用过的一个例子,这次使用的是交叉验证。我将使用cross_val_predict函数来给每个在测试切片中的数据点返回预测值。# Necessary imports: from sklearn.cross_validation import cross_val_score, cross_val_predictfrom sklearn import metrics之前,我给糖尿病数据集建立了训练测试分割,并拟合了一个模型。让我们看看在交叉验证之后的得分是多少:# Perform 6-fold cross validationscores = cross_val_score(model, df, y, cv=6)print “Cross-validated scores:”, scoresCross-validated scores: [ 0.4554861 0.46138572 0.40094084 0.55220736 0.43942775 0.56923406]正如你所看到的,最后一个子集将原始模型的得分从0.485提高到0.569。这并不是一个惊人的结果,但我们得到了想要的。现在,在进行交叉验证之后,让我们绘制新的预测图:# Make cross validated predictionspredictions = cross_val_predict(model, df, y, cv=6)plt.scatter(y, predictions)你可以看到这和原来的图有很大的不同,是原来图的点数的六倍,因为我用的cv=6。最后,让我们检查模型的R²得分(R²是一个“表示与自变量分离的可预测的因变量中方差的比例的数量”)。可以看一下我们的模型有多准确:accuracy = metrics.r2_score(y, predictions)print “Cross-Predicted Accuracy:”, accuracyCross-Predicted Accuracy: 0.490806583864本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...
基于ZStack设计一个较为简单的自动化测试系统
本文首发于泊浮目的专栏:https://segmentfault.com/blog…背景在笔者目前的项目中,大部分业务跑在基于kvm的vm上。鉴于对项目质量的追求及尽可能节省人力资源的目的,着手调研高测试覆盖率的解决方案。由于项目基于ZStack,所以架构与其较为相似,但在vm上添加了相应的agent,通过其来控制vm中的操作系统与软件。众所周知,ZStack管理节点部分有一套较为完善的自动化测试框架,可以满足大多数场景。而Utility并非如此,只能通过黑盒测试来保证其质量,且许多测试场景需要start up一个真实的ZStack的环境,测试成本较高。如果一部分测试可以设计在agent端,也就说是agent端的测试可以自举。这样就可以少设计一部分黑盒测试实例,降低潜在成本。从业务看测试从vm agent的业务逻辑来看,大多数业务都是修改DB配置、读DB配置,start, stop DB等。而为了case之间不受到影响,我们在跑完测试之后,需要重置当前的测试环境。方案选择Docker放弃。其无状态的特性简直完美契合上述需求。但是docker里只能跑单进程,对于oracle相关的业务,可能无法满足。且部分版本oracle对Docker的镜像支持上可能有问题。在docker中可以通过一些hack的手段去做到在一个容器中起多进程,比如systemd或supervisord。但是笔者对docker并不是非常的了解,所以没有继续研究下去。Pouch放弃。阿里自研的富容器技术,可以完美解决docker里单进程的问题——其实本质上也是在容器镜像起来时运行了一个init进程指定为1号进程,而不是像docker里在命令行中指定的进程。在实践过程中发现有各种莫名其妙的报错,且相关文档不齐全。VM By ZStack目前的方案。通过ZStack来管理环境vm的生命周期,通过快照机制来还原环境。缺点也非常的明显,仅仅一个case生命周期就需要1min+,生命周期大致如下:使用vm跑测试停止vm恢复快照启动vm让人感到舒服的是,ZStack提供了一套Java SDK,可以直接通过SDK完成上述操作。基于ZStack的自动化测试对接ZStack根本没有什么困难之处。更多的是要从成本这个点去考虑整个测试系统的设计。这里的成本则又可以分为两种成本:物理资源成本:产品线上目前能够投入进测试的host较少,故在资源稀缺的情况下,得尽量用较少的vm去完成这件事。时间成本:一个开发人员跑测试的时间越短越好,而不是花大量时间去等待测试环境准备、清理等。举个列子,如果我搭建一个mysql 主备库,需要两台vm,如果串行去完成生命周期相关的操作,那么就是2min+;如果并行的去做,便是1min+。同理,如果这些测试能够尽量并行的去跑,则可以省下更多的时间。角色角色示意图如下:TestCase一个测试实例,含有测试代码。同时它会向ResourcePool申请所需的vm。TestGroup根据当前的策略组织TestCase去并行的跑这些Case。在这里可以控制并发粒度,最后将会提交给Junit的跑测试。JUnitCore.runClasses(new ParallelComputer(true, true) , testGroup.toArray(arrayTestGroup));ResourcePool资源池的概念其实有点像云。比如组内有4个开发,那么4个开发几乎是不可能同时跑测试的,同时有2个开发在跑测试也是小概率事件。与其每个人都申请几台测试vm放在那边(一天可能就跑1、2小时的测试),还不如大家都共享一个池内的vm,避免资源的浪费。就像公有云卖你1core512m,你去架个网站,但是它料到你用不了这么多,所以它就会超分超卖。这个类会根据配置文件或者网络请求存入相应测试用的vm,并记录这些vm是否被使用,为了防止其他开发者一起跑测试的时候共用同一台vm,会给使用中的vm打上一个user tag,避免冲撞。另外,SessionId等常量也是从这里刷新而来。小结通过本文,向大家介绍了一下笔者目前在项目中设计的一个基于ZStack的自动化测试系统。基于各方各面的成本限制,故此设计的较为简单。如果后续有些较大的改动或显著的改进,笔者还会与大家继续分享。如果大家对于这方面的自动化测试有较好的实践或者建议,也欢迎在留言中一起交流学习。**