qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

多线程测试工具groboutils的使用

 一直使用junit做为服务测试框架,感觉不错。最近有人反映在高并发的情况下,存在服务调不到。无奈再次打开单元测试模拟高并发的情况,却发现junit不支持并发测试
  引入groboutils jar包,其实我主要使用MultiThreadedTestRunner类和TestRunnable类。
  原有的junit框架不做改变,导入GroboTestingJUnit-1.2.1-core.jar包
  代码如下
public class FaultServiceTest extends TestCase {
/**
* @param args
* @throws FaultException
* @throws ExpParamNotFoundException
* @throws ParseException
*/
private IFaultService faultService;
private static final int NUM_THREAD = 100; // 测试线程总数
public FaultServiceTest() {
super();
IInitService initService = (IInitService) CustomBeanFactory
.getBean("initService");
initService.initSiteDatabase();
this.faultService = (IFaultService) CustomBeanFactory
.getBean("faultService");
}
public FaultServiceTest(String name) {
super(name);
IInitService initService = (IInitService) CustomBeanFactory
.getBean("initService");
initService.initSiteDatabase();
this.faultService = (IFaultService) CustomBeanFactory
.getBean("faultService");
}
// 高并发测试
public void testGetEquipEventAlertListByPage() throws Throwable {
EquipmentQueryBean equipmentQueryBean = new EquipmentQueryBean();
// 生成所有测试线程
TestRunnable[] test = new TestRunnable[NUM_THREAD];
long start = System.currentTimeMillis();
for (int i = 0; i < test.length; i++) {
test[i] = new FaultServiceThread(faultService, equipmentQueryBean);
}
// 生成测试线程运行器
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(test);
// 运行测试线程
mttr.runTestRunnables();
long used = System.currentTimeMillis() - start;
System.out.printf("%s 调用花费 %s milli-seconds.\n", NUM_THREAD, used);
}
public static Test suite() {
TestSuite test = new TestSuite("HealthService接口类测试");
test.addTest(new FaultServiceTest("testGetEquipEventAlertListByPage"));
return test;
}
/*
* 测试线程类定义
*/
private static class FaultServiceThread extends TestRunnable {
private IFaultService faultService;
private EquipmentQueryBean equipmentQueryBean;
public FaultServiceThread(IFaultService faultService,
EquipmentQueryBean equipmentQueryBean) {
super();
this.faultService = faultService;
this.equipmentQueryBean = equipmentQueryBean;
}
@Override
public void runTest() throws Throwable {
faultService.getEquipEventAlertListByPage(equipmentQueryBean);
}
}

 运行代码,并发数开到100个后观察运行时间发现运行运行时间到了12秒了,看来问题出在DAO。需要进行sql代码优化了
  导入的测试包有:
import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

posted @ 2013-11-13 10:19 顺其自然EVO 阅读(436) | 评论 (0)编辑 收藏

Java读取文件方法

  1、按字节读取文件内容
/**
* 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。
*/
public static void readFileByBytes(String fileName) {
File file = new File(fileName);
InputStream in = null;
try {
System.out.println("以字节为单位读取文件内容,一次读一个字节:");
// 一次读一个字节
in = new FileInputStream(file);
int tempbyte;
while ((tempbyte = in.read()) != -1) {
System.out.write(tempbyte);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
System.out.println("以字节为单位读取文件内容,一次读多个字节:");
// 一次读多个字节
byte[] tempbytes = new byte[100];
int byteread = 0;
in = new FileInputStream(fileName);
// 读入多个字节到字节数组中,byteread为一次读入的字节数
while ((byteread = in.read(tempbytes)) != -1) {
System.out.write(tempbytes, 0, byteread);
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
}
}
}
}


2、按字符读取文件内容
/**
* 以字符为单位读取文件,常用于读文本,数字等类型的文件
*/
public static void readFileByChars(String fileName) {
File file = new File(fileName);
Reader reader = null;
try {
System.out.println("以字符为单位读取文件内容,一次读一个字节:");
// 一次读一个字符
reader = new InputStreamReader(new FileInputStream(file));
int tempchar;
while ((tempchar = reader.read()) != -1) {
// 对于windows下,\r\n这两个字符在一起时,表示一个换行。
// 但如果这两个字符分开显示时,会换两次行。
// 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
if (((char) tempchar) != '\r') {
System.out.print((char) tempchar);
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println("以字符为单位读取文件内容,一次读多个字节:");
// 一次读多个字符
char[] tempchars = new char[30];
int charread = 0;
reader = new InputStreamReader(new FileInputStream(fileName));
// 读入多个字符到字符数组中,charread为一次读取字符数
while ((charread = reader.read(tempchars)) != -1) {
// 同样屏蔽掉\r不显示
if ((charread == tempchars.length)
&& (tempchars[tempchars.length - 1] != '\r')) {
System.out.print(tempchars);
} else {
for (int i = 0; i < charread; i++) {
if (tempchars[i] == '\r') {
continue;
} else {
System.out.print(tempchars[i]);
}
}
}
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}2、按字符读取文件内容
/**
* 以字符为单位读取文件,常用于读文本,数字等类型的文件
*/
public static void readFileByChars(String fileName) {
File file = new File(fileName);
Reader reader = null;
try {
System.out.println("以字符为单位读取文件内容,一次读一个字节:");
// 一次读一个字符
reader = new InputStreamReader(new FileInputStream(file));
int tempchar;
while ((tempchar = reader.read()) != -1) {
// 对于windows下,\r\n这两个字符在一起时,表示一个换行。
// 但如果这两个字符分开显示时,会换两次行。
// 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
if (((char) tempchar) != '\r') {
System.out.print((char) tempchar);
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println("以字符为单位读取文件内容,一次读多个字节:");
// 一次读多个字符
char[] tempchars = new char[30];
int charread = 0;
reader = new InputStreamReader(new FileInputStream(fileName));
// 读入多个字符到字符数组中,charread为一次读取字符数
while ((charread = reader.read(tempchars)) != -1) {
// 同样屏蔽掉\r不显示
if ((charread == tempchars.length)
&& (tempchars[tempchars.length - 1] != '\r')) {
System.out.print(tempchars);
} else {
for (int i = 0; i < charread; i++) {
if (tempchars[i] == '\r') {
continue;
} else {
System.out.print(tempchars[i]);
}
}
}
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}


3、按行读取文件内容
/**
* 以行为单位读取文件,常用于读面向行的格式化文件
*/
public static void readFileByLines(String fileName) {
File file = new File(fileName);
BufferedReader reader = null;
try {
System.out.println("以行为单位读取文件内容,一次读一整行:");
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line = 1;
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
// 显示行号
System.out.println("line " + line + ": " + tempString);
line++;
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
  4、随机读取文件内容
/**
* 随机读取文件内容
*/
public static void readFileByRandomAccess(String fileName) {
RandomAccessFile randomFile = null;
try {
System.out.println("随机读取一段文件内容:");
// 打开一个随机访问文件流,按只读方式
randomFile = new RandomAccessFile(fileName, "r");
// 文件长度,字节数
long fileLength = randomFile.length();
// 读文件的起始位置
int beginIndex = (fileLength > 4) ? 4 : 0;
// 将读文件的开始位置移到beginIndex位置。
randomFile.seek(beginIndex);
byte[] bytes = new byte[10];
int byteread = 0;
// 一次读10个字节,如果文件内容不足10个字节,则读剩下的字节。
// 将一次读取的字节数赋给byteread
while ((byteread = randomFile.read(bytes)) != -1) {
System.out.write(bytes, 0, byteread);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (randomFile != null) {
try {
randomFile.close();
} catch (IOException e1) {
}
}
}
}
  5、将内容追加到文件尾部
public class AppendToFile {
/**
* A方法追加文件:使用RandomAccessFile
*/
public static void appendMethodA(String fileName, String content) {
try {
// 打开一个随机访问文件流,按读写方式
RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
// 文件长度,字节数
long fileLength = randomFile.length();
//将写文件指针移到文件尾。
randomFile.seek(fileLength);
randomFile.writeBytes(content);
randomFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* B方法追加文件:使用FileWriter
*/
public static void appendMethodB(String fileName, String content) {
try {
//打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
FileWriter writer = new FileWriter(fileName, true);
writer.write(content);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String fileName = "C:/temp/newTemp.txt";
String content = "new append!";
//按方法A追加文件
AppendToFile.appendMethodA(fileName, content);
AppendToFile.appendMethodA(fileName, "append end. \n");
//显示文件内容
ReadFromFile.readFileByLines(fileName);
//按方法B追加文件
AppendToFile.appendMethodB(fileName, content);
AppendToFile.appendMethodB(fileName, "append end. \n");
//显示文件内容
ReadFromFile.readFileByLines(fileName);
}
}

posted @ 2013-11-13 10:16 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

Linux操作系统内核启动参数详细解析

Linux内核在启动的时候,能接收某些命令行选项或启动时参数。当内核不能识别某些硬件进而不能设置硬件参数或者为了避免内核更改某些参数的值,可以通过这种方式手动将这些参数传递给内核。
  如果不使用启动管理器,比如直接从BIOS或者把内核文件用“cp zImage /dev/fd0”等方法直接从设备启动,就不能给内核传递参数或选项--这也许是我们使用引导管理器比如LILO的好处之一吧。
  Linux的内核参数是以空格分开的一个字符串列表,通常具有如下形式: name[=value_1][,value_2]...[,value_10]
  “name”是关键字,内核用它来识别应该把“关键字”后面的值传递给谁,也就是如何处理这个值,是传递给处理例程还是作为环境变量或者抛给“init”。值的个数限制为10,你可以通过再次使用该关键字使用超过10个的参数。
  首先,内核检查关键字是不是 `root='',`nfsroot='', `nfsaddrs='', `ro'', `rw'',`debug''或`init'',然后内核在bootsetups数组里搜索于该关键字相关联的已注册的处理函数,如果找到相关的已注册的处理函数,则调用这些函数并把关键字后面的值作为参数传递给这些函数。比如你在启动时设置参数name=a,b,c,d,内核搜索bootsetups数组,如果发“name”已注册,则调用“name”的设置函数如name_setup(),并把a,b,c,d传递给name_setup()执行。 所有型如“name=value”参数,如果没有被上面所述的设置函数接收,将被解释为系统启动后的环境变量,比如“TERM=vt100”就会被作为一个启动时参数。 所有没有被内核设置函数接收也没又被设置成环境变量的参数都将留给init进程处理,比如“single”。
  常用的设备无关启动时参数。
  1、init=...
  设置内核执行的初始化进程名,如果该项没有设置,内核会按顺序尝试/etc/init,
  /bin/init,/sbin/init, /bin/sh,如果所有的都没找到,内核会抛出 kernel panic:的错误。
  2、nfsaddrs=...
  设置从网络启动时NFS的启动地址,已字符串的形式给出。
  3、nfsroot=...
  设置网络启动时的NFS根名字,如果该字符串不是以 "/"、","、"."开始,默认指向“/tftp-boot”。
  以上2、3在无盘站中很有用处。
  4、no387
  该选项仅当定义了CONFIG_BUGi386时才能用,某些i387协处理器芯片使用32位的保护模式时会有BUG,比如一些浮点运算,使用这个参数可以让内核忽略387协处理器。
  5、no-hlt
  该选项仅当定义了CONFIG_BUGi386时才能用,一些早期的i486DX-100芯片在处理“hlt”指令时会有问题,执行该指令后不能可靠的返回操作系统,使用该选项,可以让Linux系统在CPU空闲的时候不要挂起CPU。
  6、root=...
  该参数告诉内核启动时使用哪个设备作为根文件系统。比如可以指定根文件为hda8:root=/dev/hda8。
  7、ro和rw
  ro参数告诉内核以只读方式加载根文件系统,以便进行文件系统完整性检查,比如运行fsck;rw参数告诉内核以读写方式加载根文件系统,这是默认值。
  8、reserve=...
  保留端口号。格式:reserve=iobase,extent[,iobase,extent]...,用来保护一定区域的I/O端口不被设备驱动程序自动探测。在某些机器上,自动探测会失败,或者设备探测错误或者不想让内核初始化设备时会用到该参数;比如: reserve=0x300,32device=0x300,除device=0x300外所有设备驱动不探测 0x300-0x31f范围的I/O端口。
  9、mem=...
  限制内核使用的内存数量。早期BIOS设计为只能识别64M以下的内存,如果你的内存数量大于64M,你可以指明,如果你指明的数量超过了实际安装的内存数量,系统崩溃是迟早的事情。如:mem=0x1000000意味着有16M内存,如果是mem=0x6000000,就是96M内存了。 注意:很多机型把部分内存作为BIOS的映射,所以你在指定内存大小的时候一定要预留空间。你也可以在 pentium或者更新的CPU上使用mem=nopentium关闭4M的页表,这要在内核配置时申明。

 10、panic=N
  默认情况,内核崩溃--kernel panic 后会宕机而不会重启,你可以设置宕机多少秒之后重启机器;也可以在/proc/sys/kernel/panic文件里设置。
  11、reboot=[warm|cold][,[bios|hard]]
  该选项仅当定义了CONFIG_BUGi386时才能用。2.0.22的内核重启默认为cool reboot,warm reboot 更快,使用"reboot=bios"可以继承bios的设置。
  12、nosmp 和 maxcpus=N
  仅当定义了 __SMP__,该选项才可用。可以用来禁用多CPU或者指明最多支持的CPU个数。
  内核开发和调试的启动时参数
  这些参数主要用在内核的开发和调试上,如果你不进行类似的工作,你可以简单的跳过本小节。
  1、debug
  Linux的日志级别比较多(详细信息可以参看Linux/kernel.h),一般地,日志的守护进程klogd只把比DEBUG级别高的日志写进磁盘;如果使用该选项,klogd也把内核的DEBUG信息写进日志。
  2、profile=N
  在做内核开发的时候,如果想清楚的知道内核在什么地方耗用了多少CPU的时钟周期,可以使用核心的分析函数设置变量prof_shift为非0值,有两种方式可以实现:一种是在编译时指定,另一种就是通过“profile=”来指定; 他给出了一个相当于最小单位--即时钟周期;系统在执行内核代码的时候,profile[address >;>; prof_shift]的值就会累加,你也可以从/proc/profile得到关于它的一些信息。
  3、swap=N1,N2,N3,N4,N5,N6,N7,N8
  设置内核交换算法的八个参数:max_page_age, page_advance, page_decline,page_initial_age,age_cluster_fract, age_cluster_min, pageout_weight,bufferout_weight。
  4、buff=N1,N2,N3,N4,N5,N6
  设置内核缓冲内存管理的六个参数:max_buff_age, buff_advance, buff_decline,buff_initial_age, bufferout_weight, buffermem_grace。
  使用 RAMDISK的参数 (仅当内核配置并编译了CONFIG_BLK_DEV_RAM)。
  一般的来说,使用ramdisk并不是一件好事,系统自己会更加有效的使用可用的内存;但是,在启动或者制作启
  动盘时,使用ramdisk可以很方便的装载软盘等设备上的映象(尤其是安装程序、启动过程中),因为在正真使用物理磁盘之前,必须要加载一些必要的模块,比如文件系统模块,scsi驱动等(可以参见我的initrd-x.x.x.img文件分析-制作安装程序不支持的根文件系统)。
  早期的ramdisk(比如1.3.48的核心)是静态分配的,必须以ramdisk=N来指定ramdisk的大小;现在ramdisk可以动态增加。一共有四个参数,两个布尔型,两个整形。
  1、load_ramdisk=N
  如果N=1,就加载ramdisk;如果N=0,就不加载ramdisk;默认值为0。
  2、prompt_ramdisk=N
  N=1,提示插入软盘;N=0,不提示插入软盘;默认为1。
  3、ramdisk_size=N或者ramdisk=N
  设定ramdisk的最大值为N KB,默认为4096KB。
  4、ramdisk_start=N
  设置ramdisk的开始块号为N,当ramdisk有内核的映象文件是需要这个参数。
  5、noinitrd
  (仅当内核配置了选项CONFIG_BLK_DEV_RAM和CONFIG_BLK_DEV_INITRD)现在的内核都可以支持initrd了,引导进程首先装载内核和一个初始化的ramdisk,然后内核将initrd转换成普通的ramdisk,也就是读写模式的根文件系统设备。然后Linuxrc执行,然后装载真正的根文件系统,之后ramdisk被卸载,最后执行启动序列,比如/sbin/init。 选项noinitrd告诉内核不执行上面的步骤,即使内核编译了initrd,而是把initrd的数据写到 /dev/initrd,只是这是一个一次性的设备。


posted @ 2013-11-13 10:06 顺其自然EVO 阅读(648) | 评论 (0)编辑 收藏

Maven配置之pom文件配置包含和排除测试

 包含(Inclusions )
  默认情况下Surefire Plugin会自动的include下面这些格式的类:
  “**/Test*.java” – includes所有以Test开头的java文件
  “**/*Test.java” – includes所有以Test结尾的java文件
  “**/*TestCase.java” – includes所有以TestCase结尾的java文件
  当然我们也可以执行其它名字的类,或者说如果只想执行某一个或某几个用例,可以通过配置include来配置,如下:
  排除(Exclusions )
  在mvn test的时候不想执行哪些用例,便可以通过exclude属性来配置,如下:
  支持正则表达式(Regular expression support)
  include/ecxlude属性支持类似ant风格的路径表达式,也支持正则表达式,语法如下:
  注意语法 %regex[expr],这里expr是真正的表达式,另外请注意正则匹配.class文件而不是.java文件
相关文章
Maven 编译打包时如何忽略测试用例

posted @ 2013-11-12 11:17 顺其自然EVO 阅读(160) | 评论 (0)编辑 收藏

自动化的路怎么走,让我们迈出懦弱的一步吧

 曾经我们觉得自动化很神奇,曾经我们觉得自动化很难,曾经我们觉得掌握了QTP就掌握了自动化,若干年过去后,我们看看自己还在追赶国外的技术,我们真的有那么大的差距么?我想说的是未必!
  大家可以看看行业中有名的那本自动化经典之书《QTP自动化测试权威指南》(印度人写的),在没看过之前,在我没有深入自动化之前,我也觉得我们离得可能很远,但是等我看到原版,看到翻译版,我觉得just so so,技术?我们从来没有学不过别人的,而唯一缺的却是胆量!就是认可自己的想法,认可自己的判断,而不是简单的重复前人走过的道路,我们需要勇敢的面对自己的判断能力,迂腐的把责任都推给别人。
  常常我们会觉得IBM、HP的测试工具代表了测试的顶端,而其实他们就不是一个测试公司,而测试产品不过也是并购别的测试工具公司。回头看看包括我自己也是如此,总是觉得国外的月亮比自己的圆!但是在我看到越来越多客户在使用国外测试软件中遇到的问题时,我开始彷徨;当我开始分析和设计自己的测试工具的时候,我也逐渐找到了信心,而我现在想说的是,我们应该相信自己,特别是当我看到恒大赢球的那一刻,我勇敢的写下了现在的这篇文章,让我们相信自己,而不要总是徘徊吧。
  下面我从4方面来说说我对自动化的理解:
  A.为什么要做自动化
  手工测试逐渐被大家定义为了低技术含量的工作,而确实大规模的手工测试不但效率低下而且质量还得不到保证。通过自动化测试,我们可以解决两个问题:
  1.相同成本下,自动化能做的比手工更好
  2.相同效果下,自动化能比手工更省钱
  对于现在很多行业中需要短时间大规模的发布,传统的瀑布模型长周期测试已经完全不能支持,自动化测试成为了必修之课。
  B.自动化到底能做什么
  其实自动化对于现在国内的企业来说,大多数情况下,是毫无用处的,主要原因在何处?相信做了自动化测试几年后的朋友会有点感受,对一个正确的东西跑100次还是正确的,除了能够看到一个报告能够满足一下自己YY的心理想法和获得一个自我心理安慰意外,真的提高了软件质量么?
  没有!问题在什么地方?就是使用工具的人和使用工具的目的产生了偏差,偏差在于手工测试的设计没有达到标准,如果没有办法有效的测试用例,自动化的效果会非常的低,而另一方面为了自动化而自动化,既然没有能力控制版本变化,那么就用自动化做全面回归测试吧,这样看起来很Cool。
  其次自动化并不是什么高深的东西,但是往往由于在研发中没有考虑到自动化的一些需要,让自动化做起来总是那么的别扭,简单说就是总是让测试去配合开发的不规范,而不是反过来。做过单元测试的朋友应该深深理解这句话,你这个东西就做的不规范让别人怎么测,还得配合你这个来测?
  我们现在要做自动化,并不是简单购买个工具就行的,而是真正成熟了团队、流程、去选择自动化工具来适合自己的团队,而且购买工具只是开始,实现自动化体系才是解决问题的关键。而自动化体系的实现无非就是两种:
  1.招一个高手,自己内部探索实现
  2.通过第三方咨询模式
  C.自动化框架应该怎么做
  首先要提一个问题,为什么要做自动化框架?
  对于所有自动化工具来说都提供自身框架,但是这些框架都只能做到方面用户使用,却无法做到真正的自动化体系,该体系就是打通自动化与各个系统的关系,而不是让自动化执行成为自动化唯一的孤岛。
  自动化体系需要体现的是从需求到研发,从研发到发布,从发布到测试,从测试到分析统计等方面的全方位自动化,将人从繁重的执行工作和收集整理数据的工作中解放出来,把精力放在设计上,这才是自动化测试的目标。而自动化框架就是要解决工具的孤岛问题,将业务和技术分离,一方面让了解技术的人可以将复杂的技术简化给业务人员来用,另一方面将周边的各个系统都整合在一起,做到全方面的自动化。
  简单来说例如”微信”能解决你简单的查账、支付,但是它仍然是一个信息孤岛,能不能进一步的成为账号互相支付,理财,信用卡还款等一个完全的集中式的金融中心呢?其实支付宝已经做到了部分,现在出来了一个叫做”来往”的客户端,看”来往”能做成什么样吧!
  每个公司都有自己的业务特点和使用习惯,自动化框架就是在这个基础上定制一套适合自身体系的流程自动化工具,其中困难的地方是在自动化流程的梳理和自动化工具的定制方面。首先公司很难有一个能在大局上把握住自动化流程体系的人物,其次由于大多数开发都对测试并不怎么认可,所以开发一个测试工具在需求沟通上就会存在很大的问题,往往测试人员由于自身开发能力的不足导致自动化框架在架构上设计上存在较多的问题。
  最终导致很多框架做出来要么是技术有余业务不足,要么就是业务有余技术不足。

D .怎么去在公司开展自动化
  从上面两点来看第三点就比较清晰了,对于公司来说开展自动化并不是购买一个工具,而是全方面的评估考察,来帮助公司获得最佳的ROI(投资回报比)。
  工具永远都是最后考虑的内容,而整个系统需要自动化支持的原因,希望达到的目标,团队的规划是首先应该考虑的,根据公司被测系统的特点和相关人员的技术情况进行自动化工具评估,再逐步进入采购后的落地。首先将关键业务通过自动化实现回归,后逐步提高自动化覆盖率并加入其它自动化模块进入框架设计。
  当然在这其中你可能会遇到一些困难,因为一个人很难改变行业中的某些舆论,例如:
  1.选一个工具就选主流吧,QTP错不了!
  做一个简单的事情其实用什么都差不多,适合自己情况的才是最好的,而未必是某一个工具。
  2.我就算不选QTP,我有别的选的么?有,但是我不知道
  行业的宣传导致了这个结果,我们简单的认为了QTP就是自动化,自动化就是QTP,还好最近两年Selenium的异军突起,让我们开始认可或者正视QTP的霸主地位正在动摇。
  3.我不选QTP招不到人怎么办?
  作为一个招聘企业,其实换个角度来看更容易招聘到优秀的人才,别的公司都在招聘QTP人员,这类培训或者学习的人很多,但是素质、基础其实存在相对较差的情况,而自动化本身是很简单的,学会一个自动化工具再去学习别的自动化工具非常容易,更重要的学习和理解的能力,如果招聘在这方面另辟蹊径,反而可以从众多的简历中选择出真正理解自动化测试的人员,将招聘的主动权拿在自己手里,而对于员工来说也对公司的更加认可。
  最后还是来说几句心里话:
  主流只是现在,不是未来,在我看来谁主动进入编程开放的年代,谁掌握了未来,QTP你已经Out了,因为你的懦弱,QTP just a tool,not mind,not soul。
  选择工具需要正确的比较,是要有真材实料而不是小便宜策略,开放式的病毒营销对于普通用户可能有效,对于专业用户是没有用的,我们应该来拼实力而不是所谓的宣传。
  不要等待别人来配合你,你应该推动行业,谁走在前面,谁能收获优秀的人员,谁能获胜,选择庸俗的人员只能让你的团队庸俗,带一群猪一样的队友怎么去战胜神一样的对手!
版权声明:本文出自 云层 的51Testing软件测试博客:http://www.51testing.com/html/04/104-854102.html
原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。

posted @ 2013-11-12 11:16 顺其自然EVO 阅读(165) | 评论 (0)编辑 收藏

使用jmeter测试java程序

 最近在用jmeter进行性能测试,防止被忘记,把步骤写下。
  场景:测试java程序
  1、右击测试计划-> 添加 -> Threads(Users) -> 线程组
  2、设置线程属性,用于并发请求。
  介绍:
  线程数: 5
  Ramp-Up Period(in seconds) : 1
  循环次数: 2
  含义:1秒种起动5个线程,每个线程循环调用2次java请求
  3、线程组右击 -> 添加 -> Sampler -> Java请求
  之前建立测试类:
  Java代码
package com.my.test;
import java.util.Random;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
/**
* Hello world!
*
*/
public class App  extends AbstractJavaSamplerClient{
public static double computer(double a,double b){
return a * b / a * a;
}
@Override
public SampleResult runTest(JavaSamplerContext arg0) {
// TODO Auto-generated method stub
SampleResult sr = new SampleResult();
sr.sampleStart();
Random random = new Random();
for(int i=0; i<10000; i++){
computer(random.nextDouble(),random.nextDouble());
}
sr.setSuccessful(true);
sr.sampleEnd();
return sr;
}
@Override
public void setupTest(JavaSamplerContext context) {
// TODO Auto-generated method stub
super.setupTest(context);
}
@Override
public void teardownTest(JavaSamplerContext context) {
// TODO Auto-generated method stub
super.teardownTest(context);
}
}
  使用的jar包有:ApacheJMeter_java.jar ,ApacheJMeter_core.jar
  上述jar包在 %JMETER_HOME%/lib/ext/下
  将测试程序打成jar包,放在%JMETER_HOME%/lib/ext/下
  这里会自动识别出该类,选择。
  4、线程组右击 -> 添加 -> 监听器 -> 用表格察看结果
  Ctrl + R,开始运行,
  Ctrl + E,清除历史结果

posted @ 2013-11-12 11:04 顺其自然EVO 阅读(348) | 评论 (0)编辑 收藏

Java web项目用cookie记住用户名、密码

 web 项目中一般在登录的时候都会用到记住密码功能。
  1.jsp页面:
<% String flag = (String)session.getAttribute("flag")==null?"":(String)session.getAttribute("flag");
String name = "";
String password = "";
try{
Cookie[] cookies=request.getCookies();
if(cookies!=null){
for(int i=0;i<cookies.length;i++){
if(cookies[i].getName().equals("cookie_user")){
String value =  cookies[i].getValue();
if(value!=null&&!"".equals(value)){
name=cookies[i].getValue().split("-")[0];
if(cookies[i].getValue().split("-")[1]!=null && !cookies[i].getValue().split("-")[1].equals("null")){
password=cookies[i].getValue().split("-")[1];
}
}
}
request.setAttribute("name",name);
request.setAttribute("passward",password);
}
}
}catch(Exception e){
e.printStackTrace();
}
%>
<body>
<div id="logo"><img src="<%=request.getContextPath() %>/frontsite/Images/Logo.jpg" width="244" height="44" alt="" /></div>
<div id="loginfrm">
<form action="<%=request.getContextPath() %>/frontsite/login.do?method=login" method="post" id="forms" onsubmit="checkForm();return false;">
<div class="frm">
<ul>
<li class="l1">用户名:</li>
<li class="l2"><input name="login_id" type="text" id="login_id" class="input" value="<%=name %>"/></li>
<li class="l3">* 最大限度为20字节</li>
<li class="l1">密码:</li>
<li class="l2"><input name="login_pwd" type="password" id="login_pwd" class="input" value="<%=password %>"/></li>
<li class="l3">* 最大限度为20字节</li>
<li class="l1">验证码:
<li class="l2" style="width: 180px;"><input name="checkImg" id="checkImg" type="text" class="input" style="width: 60px;" size="10" />
&nbsp;&nbsp;<img src="<%=request.getContextPath() %>/CheckImg_FT" width="49" height="22" /></li>
<li class="l4">
<label><input type="checkbox" name="flag" id="flag" value="1" <%if(flag!=null && flag.equals("1")){%> checked ; value ="1"; <%}else {%> value="0" <%;}%> />记住密码</label>
<label><span style="margin-left:10px; color: #F00;" ><html:errors /></span></label>
</li>
<li class="l4"><input type="image" name="imageField" id="imageField" src="<%=request.getContextPath() %>/frontsite/Images/login_bnt.jpg" /></li>
</ul>
</div>
</form>
</div>
</body>


  2.java 类:
String flag = request.getParameter("flag");
//set cookie
if(flag!=null && flag.equals("1")){
Cookie cookie = new Cookie("cookie_user", po.getLogin_id()+"-"+form.getLogin_pwd());
cookie.setMaxAge(60*60*24*30); //cookie 保存30天
response.addCookie(cookie);
}else{
Cookie cookie = new Cookie("cookie_user",po.getLogin_id()+"-"+null);
cookie.setMaxAge(60*60*24*30); //cookie 保存30天
response.addCookie(cookie);
}

posted @ 2013-11-12 11:02 顺其自然EVO 阅读(2293) | 评论 (0)编辑 收藏

关于Button点击弹出输入框,并且通过Servlet连接数据库

 应主管的要求,做了一个月的Android,对Android也发生了许多的问题,下面是主管让我弄的一个弹出输入框,能够输入评论,保存到数据库的功能
  这是MainActivity类中的内容
// 评论按钮
comment = (Button) findViewById(R.id.comment);
comment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// LayoutInflater inflater = getLayoutInflater();
// final View layout = inflater.inflate(R.layout.comment,(ViewGroup) findViewById(R.id.dialog));
// 获取文本
final EditText inputServer = new EditText(MainActivity.this);
// 设置获取文本内容为true
inputServer.setFocusable(true);
if(MainActivity.CityName == null){
inputServer.setText("你还未搜索当前城市,请定位后再评论");
}
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("评价框").setView(inputServer);
builder.setNegativeButton("取消", null);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface arg0, int arg1) {
String text = inputServer.getText().toString();
if(MainActivity.CityName == null){
HttpGet httpGet = new HttpGet("http://192.168.11.135:8081/MySql/pinlunServlet?cityName=null&comment=" + text);
try {
HttpResponse response = new DefaultHttpClient().execute(httpGet);
if(response.getStatusLine().getStatusCode() < 2000){
Log.i("add", "评论成功");
}else{
Log.e("Err", "评论失败");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else{
HttpGet httpGet = new HttpGet("http://192.168.11.135:8081/MySql/pinlunServlet?cityName=" + MainActivity.CityName + "&comment=" + text);
try {
HttpResponse response = new DefaultHttpClient().execute(httpGet);
if(response.getStatusLine().getStatusCode() < 2000){
Log.i("add", "评论成功");
}else{
Log.e("Err", "评论失败");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("add",text);
}});
builder.show();
}
});

 这是comment.xml中的配置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="#ffffffff" android:orientation="horizontal"
android:id="@+id/dialog">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/tvname"
android:text="评论:" />
<EditText
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/etname"
android:minWidth="100dip"/>
</LinearLayout>
AndroidManifest.xml中权限(仅供参考,如有缺少可以添加)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.baudumap"
android:versionCode="1"
android:versionName="1.0" >
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="18" >
</uses-sdk>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.example.baudumap" >
</instrumentation>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" >
</uses-permission>
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" >
</uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" >
</uses-permission>
<uses-permission android:name="android.permission.READ_LOGS" >
</uses-permission>
<application
android:allowBackup="true"
android:icon="@drawable/wing"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="android.test.runner" />
<activity
android:name="com.example.baudumap.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote" >
</service>
</application>
</manifest>

posted @ 2013-11-12 11:00 顺其自然EVO 阅读(368) | 评论 (0)编辑 收藏

测试用例设计与管理思路整理

 简单7个步骤:
  1、理清模块需求 :
  ----由于项目需求说明书不详细,而且没有进行需求评审的情况下,在拿到上级lead给的测试任务后,一拿到先别着急去写测试用例,首先你应该做的是,根据有限的模块需求说明进行深入理解模块的功能,流程,以及涉及到的其他功能,记录下来。发送给该模块的 开发 人员,询问他你理解的是否和他设计的有差错,虽然说开发人员可能对整个需求不情况,但是对自己要开发的模块肯定还是能说出个大概来。
  2、测试需求提起
  -----在经过和相对应的开发人员简单交流后,就可以根据得到文档进行测试需求提起了,原则是从大到小,大模块一直分解到最小部分模块。整理一份模块测试需求书
  3、设计测试思路
  -----测试需求书完成后,就可以设计测试思路,这里的设计思路并不是说写测试用例,而是一个总的思路说明;
  4、测试用例编写
  ----头脑风暴完成后,就可以整理出一份测试思路,最好在设计 测试用例模板 时考虑到这点,只有把思路记录下来,在后面的详细用例编写中才不会忘记,在后期的维护用例中也可以快速掌握用例情况。后面会有一份测试用例模板
  5、测试用例评审
  ---------这一步就不说了,如果有时间的话最好做详细的用例评审,没有时间的话也要进行测试内部人员相互查看各自的用例,提出各自的意见。
  6、执行用例
  --------这一步是最好检验测试用例编写的水平了,交叉进行用例执行。
  7、用例效率计算
  -------这一步对有很好 测试管理 工具的公司来说,可能没有用处。这里是根据公司进行设计的,由于公司不是很大,也没有用大型商业测试管理工具,所以一下用例效率都可能必须手动,公司用Jira管理 Bug ,用例和需求都是通过Excel进行管理。在需求与用例之间暂时没有想到好的方法,用例与Bug对应已经想出方法了,在下面的的用例模板中有。
  虽然说对应比较简单,但是比较实用,能够快速反应出用例设计的 质量 ,以及用例是否遗漏了。

posted @ 2013-11-12 10:39 顺其自然EVO 阅读(242) | 评论 (0)编辑 收藏

gtest中如何跳出当前测试案例

 在Apache服务器的套件中,有一个叫做 ab (ApacheBench) 的工具。
  ApacheBench 主要是用来测试Apache服务器执行效率用的
  ApacheBench 可以针对某个特定的 URL 仿真出连续的联机请求
  同时还可以仿真出同时间点数个相同的联机请求,因此利用 ApacheBench 可帮助我们在网站开发期间仿真实际上线可能的情况,利用仿真出来的数据做为调整服务器设定或程序的依据。
  参数说明
01bixiaopeng@172-13-3-157 ~$ ab -h
02Usage: ab [options] [http[s]://]hostname[:port]/path
03Options are:
04//-n 在测试会话中所执行的请求个数。默认时,仅执行一个请求
05   -n requests     Number of requests to perform
06//-c 一次产生的请求个数。默认是一次一个。
07   -c concurrency  Number of multiple requests to make
08//-t 测试所进行的最大秒数。其内部隐含值是-n 50000。它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。
09   -t timelimit    Seconds to max. wait for responses
10
11   -b windowsize   Size of TCP send/receive buffer, in bytes
12//-p 包含了需要POST的数据的文件.
13   -p postfile     File containing data to POST. Remember also to set -T
14
15   -u putfile      File containing data to PUT. Remember also to set -T
16//-T POST数据所使用的Content-type头信息。
17   -T content-type Content-type header for POSTing, eg.
18                   'application/x-www-form-urlencoded'
19                   Default is 'text/plain'
20//-v 设置显示信息的详细程度 - 4或更大值会显示头信息, 3或更大值可以显示响应代码(404, 200等), 2或更大值可以显示警告和其他信息。 -V 显示版本号并退出。
21   -v verbosity    How much troubleshooting info to print
22//-w 以HTML表的格式输出结果。默认时,它是白色背景的两列宽度的一张表。
23   -w              Print out results in HTML tables
24//-i 执行HEAD请求,而不是GET。
25   -i              Use HEAD instead of GET
26   -x attributes   String to insert as table attributes
27   -y attributes   String to insert as tr attributes
28   -z attributes   String to insert as td or th attributes
29//-C cookie-name=value 对请求附加一个Cookie:行。 其典型形式是name=value的一个参数对。此参数可以重复。
30   -C attribute    Add cookie, eg. 'Apache=1234. (repeatable)
31   -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
32                   Inserted after all normal header lines. (repeatable)
33   -A attribute    Add Basic WWW Authentication, the attributes
34                   are a colon separated username and password.
35//-P proxy-auth-username:password 对一个中转代理提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即, 是否发送了401认证需求代码),此字符串都会被发送。
36   -P attribute    Add Basic Proxy Authentication, the attributes
37                   are a colon separated username and password.
38   -X proxy:port   Proxyserver and port number to use
39   -V              Print version number and exit
40   -k              Use HTTP KeepAlive feature
41   -d              Do not show percentiles served table.
42   -S              Do not show confidence estimators and warnings.
43   -g filename     Output collected data to gnuplot format file.
44   -e filename     Output CSV file with percentages served
45   -r              Don't exit on socket receive errors.
46   -h              Display usage information (this message)
47   -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)
48   -f protocol     Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL)
49
50//-attributes 设置 属性的字符串. 缺陷程序中有各种静态声明的固定长度的缓冲区。另外,对命令行参数、服务器的响应头和其他外部输入的解析也很简单,这可能会有不良后果。它没有完整地实现 HTTP/1.x; 仅接受某些'预想'的响应格式。 strstr(3)的频繁使用可能会带来性能问题,即, 你可能是在测试ab而不是服务器的性能。


 具体分析
01 //-c每次发送10个,-n 共发送1000次请求
02
03 bixiaopeng@bixiaopengtekiMacBook-Pro ~$ ab -c 10 -n 1000 http://www.wirelessqa.com/?p=143
04 This is ApacheBench, Version 2.3 <$Revision: 655654 $>
05 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
06 Licensed to The Apache Software Foundation, http://www.apache.org/
07
08 Benchmarking www.wirelessqa.com (be patient)
09 Completed 100 requests
10 Completed 200 requests
11 Completed 300 requests
12 Completed 400 requests
13 Completed 500 requests
14 Completed 600 requests
15 Completed 700 requests
16 Completed 800 requests
17 Completed 900 requests
18 Completed 1000 requests
19 Finished 1000 requests
20
21 //服务器软件IIS
22 Server Software:        IIS
23 //服务器域名
24 Server Hostname:        www.wirelessqa.com
25 //服务器端口
26 Server Port:            80
27
28 //文档路径
29 Document Path:          /?p=143
30 //文档的长度,不包括响应头
31 Document Length:        40865 bytes
32
33 //并发数
34 Concurrency Level:      10
35 //整个测试持续时间
36 Time taken for tests:   349.234 seconds
37 //完成请求数量
38 Complete requests:      1000
39 //失败请求数量674
40 //Failed requests给出了失败原因统计:
41 //Connect:无法送出请求,目标主机连接失败,要求过程中连线被中断
42 //Length:回应的内容长度不一致,以content-Length头文件为判断依据
43 //Exception:发生了无法预期的错误
44 //备注1:这里分析一下为何失败了674次,从下面的统计数据我们可以看出,失败请求都落在Length上面,原因是被测网页是动态内容,当第一次发出HTTP request与后面发出的HTTP request所响应的html Length是不同大小的(即每次响应的Content-Length大小不一致),所以才会引起Length失败,这里Length是以第一次取得的Content-Length为主,以后得到的Content-Length跟第一次的不一致,就会报Length错误,所以这类Length大小不一的失败在测试动态面面的压力测试中是合理的,可以不用管它。
45 Failed requests:        674
46   (Connect: 0, Receive: 0, Length: 674, Exceptions: 0)
47 Write errors:           0
48
49 //整个场景中的网络传输量
50 Total transferred:      41114189 bytes
51 //整个场景中的HTML内容传输量
52 //备注2:单个html文件的大小为40865,共发出1000次请求,40865000跟40889965差不多,所以1000次请求应该已经正确送出,没什么问题
53 HTML transferred:       40889965 bytes
54 //关键指标之一,相当于 LR 中的 每秒事务数 ,后面括号中的 mean 表示这是一个平均值
55 Requests per second:    2.86 [#/sec] (mean)
56 //关键指标之二,相当于 LR 中的 平均事务响应时间 ,后面括号中的 mean 表示这是一个平均值
57 Time per request:       3492.343 [ms] (mean)
58 //每个请求实际运行时间的平均值,就是上面的时间 除以并发数
59 Time per request:       349.234 [ms] (mean, across all concurrent requests)
60
61
62 //平均每秒网络上的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题
63 Transfer rate:          114.97 [Kbytes/sec] received
64
65 //网络上消耗的时间的分解
66 Connection Times (ms)
67              min  mean[+/-sd] median   max
68 Connect:       35  161 401.6     59    3394
69 Processing:   571 3282 2723.1   2648   28374
70 Waiting:      309  654 767.3    538   21420
71 Total:        610 3443 2777.4   2826   28514
72
73 //整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,其中50%的用户响应时间小于2826 毫秒,66% 的用户响应时间小于3636 毫秒,最大的响应时间小于28514毫秒
74 Percentage of the requests served within a certain time (ms)
75  50%   2826 // 就是有50%的请求都是在2826ms内完成的
76  66%   3636
77  75%   4364
78  80%   4799
79  90%   6239
80  95%   7744
81  98%  11263
82  99%  14251
83 100%  28514 (longest request)

posted @ 2013-11-11 10:27 顺其自然EVO 阅读(291) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 184 185 186 187 188 189 190 191 192 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜