无论怎么小心,想完全避免bad code是不可能的,此时就需要一些工具来帮助我们检查代码中是否存在会造成内存泄漏的地方。
Android tools中的DDMS就带有一个很不错的内存监测工具Heap(这里我使用eclipse的ADT插件,并以真机为例,在模拟器中的情况类似)。用 Heap监测应用进程使用内存情况的步骤如下:
1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的;
2. 将
手机通过USB链接至电脑,链接时需要确认手机是处于“USB调试”模式,而不是作为“Mass Storage”;
3. 链接成功后,在DDMS的Devices视图中将会显示手机设备的序列号,以及设备中正在运行的部分进程信息;
4. 点击选中想要监测的进程,比如system_process进程;
5. 点击选中Devices视图界面中最上方一排图标中的“Update Heap”图标;
6. 点击Heap视图中的“Cause GC”按钮;
7. 此时在Heap视图中就会看到当前选中的进程的内存使用量的详细情况。
说明:
a) 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作;
b) 当内存使用信息第一次显示以后,无须再不断的点击“Cause GC”,Heap视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化;
c) 内存使用信息的各项参数根据名称即可知道其意思,在此不再赘述。
如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对 象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则data object的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,
直到到达一个上限后导致进程被kill掉。
d) 此处已system_process进程为例,在我的
测试环境中system_process进程所占用的内存的data object的Total Size正常情况下会稳定在2.2~2.8之间,而当其值超过3.55后进程就会被kill。
总之,使用DDMS的Heap视图工具可以很方便的确认我们的程序是否存在内存泄漏的可能性。
版权声明:本文出自 smalllin 的51Testing软件测试博客:http://www.51testing.com/?344504
原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。
1.Socket是代表两台机器之间网络连接的对象(java.net.Socket)。 Socket的建立如下,参数分别是服务器端的IP地址和端口号: Socket socket = new Socket("167.5.75.1",5000);
2.客户端(Client)Socket的使用
2.1 从Socket读出数据步骤:
// 1.创建Socket连接,告知 Server的IP地址以及端口号 Socket socket = new Socket("127.0.0.1", 4242); // 2.创建InputStreamReader,用于读取socket输入流 InputStreamReader stream = new InputStreamReader(socket.getInputStream()); // 3.使用BufferedReader链接输入流 BufferedReader br = new BufferedReader(stream); // 4.读出数据 String line = null; while ((line = br.readLine()) != null) { System.out.println("Today's advice is: " + line); } // 5. 关闭输入流BufferedReader br.close(); 2.2 向Scoket写入数据步骤: // 1.创建Socket连接,告知Server的IP地址以及端口号 Socket socket = new Socket("127.0.0.1", 4242); // 2.创建PrintWriter对象,用以接收socket输出流 PrintWriter writer = new PrintWriter(socket.getOutputStream()); // 3.使用PrintWriter对象写出输出数据 String advice ="Today's advice"; writer.println(advice); // 4. 关闭连接 writer.close(); 3. 服务器端(Server)Socket的使用 // 1.创建一个SercerSocket,使用4242端口监听客户端请求 ServerSocket serverSocket = new ServerSocket(4242); System.out.println("The server is started, listening on port 4242"); while (true) { // 2.ServerSocket的accept()在等待用户连接的时候闲置;在用户连接上来的时候,返回一个Socket来与客户端通信 Socket socket = serverSocket.accept(); // 3.创建PrintWriter对象,用以接收socket输出流 PrintWriter writer = new PrintWriter(socket.getOutputStream()); // 4.使用PrintWriter对象写出输出数据 String advice = "notifier's blog"; writer.println(advice); // 5. 关闭连接 writer.close(); } |
4. 线程的状态
线程总共有5种状态:
1. 新建 (Thread t = new Thread())
2. 就绪 (t.start())
3. 运行
4. 堵塞
线程被block的原因很多,比如: 等待IO操作, sleep(), 等待被占用对象释放
5.死亡
5.解决线程同步化问题的方法是: 对使用到共享对象的方法使用synchronized
需要注意的是:
虽说是方法进行了synchronized,但锁不是加在方法上的而是对象上的,也就是说,是synchronized方法获取对象锁。如果对象(类)有两个或者多个synchronized方法,就表示两个线程不能同时进入同一个方法,也不能同时进入不同的方法。 因为同一时间,只有一个方法在占有对象锁。
6.synchronized代码块
有时候在一个方法中做了很多事情,但只有一部分逻辑是需要synchronized的,这时候我们可以使用synchronized代码块。如下,其中this表示当前对象:
public void function() { doSomething(); //以下方法需要同步化 synchronized (this) { doCriticalStuff(); moreCriticalStuff(); } doSomeOtherThing(); } |
7. 以下是一个Socket简单的例子:
客户端代码及详细注释:
/** * @author notifier * @create 2010-9-25 上午10:12:10 * @version 1.0 */ public class DailyAdviceClient { public static void main(String[] args) { DailyAdviceClient client = new DailyAdviceClient(); client.receiveMsg(); } public void receiveMsg() { try { // 1.创建Socket连接,告知Server的IP地址以及端口号 Socket socket = new Socket("127.0.0.1", 4242); // 2.创建InputStreamReader,用于读取socket输入流 InputStreamReader stream = new InputStreamReader(socket .getInputStream()); // 3.使用BufferedReader链接输入流 BufferedReader br = new BufferedReader(stream); // 4.读出数据 String line = null; while ((line = br.readLine()) != null) { System.out.println("Today's advice is: " + line); } // 5. 关闭输入流BufferedReader br.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } |
服务器端代码及详细注释:
/** * @author notifier * @create 2010-9-25 下午07:06:54 * @version 1.0 */ public class SimpleChatServer { // 保存客户端列表 private ArrayList clientList = new ArrayList();; public static void main(String[] args) { new SimpleChatServer().startUp(); } /** * 负责服务器端的启动 * */ public void startUp() { try { // 创建服务器端ServerSocket连接,监听端口号5000 ServerSocket serverSocket = new ServerSocket(5000); // 轮询等待客户端请求 while(true) { // 等待客户端请求,无请求则闲置;有请求到来时,返回一个对该请求的socket连接 Socket clientSocket = serverSocket.accept(); // 将该客户端加入到列表中 PrintWriter writer = new PrintWriter(clientSocket.getOutputStream()); clientList.add(writer); // 创建ClientHandler对象,通过socket连接通信 Thread t = new Thread(new ClientHandler(clientSocket)); t.start(); System.out.println("有Client连进来"); } }catch(Exception e) { e.printStackTrace(); } } /** * 客户端处理类, 主要负责: * 1.接收客户端发来的消息 * 2.将消息转发其他客户端 * @author sdniu * @create 2010-9-26 上午10:00:18 * @version 1.0 */ public class ClientHandler implements Runnable { private BufferedReader reader; private Socket socket; /** * ClientHandler的构造函数 * @param clientSocket */ public ClientHandler(Socket clientSocket) { try { // 得到socket连接 socket = clientSocket; // 得到客户端发来的消息 InputStreamReader isReader = new InputStreamReader(socket.getInputStream()); reader = new BufferedReader(isReader); } catch (IOException e) { e.printStackTrace(); } } public void run() { String message; try { while((message = reader.readLine()) != null) { System.out.println("客户端消息: " + message); // 将客户端发来的消息转发所有客户端 notifyAllClients(message); } } catch (IOException e) { e.printStackTrace(); } } } /** * * @param message */ public void notifyAllClients(String message) { // 得到客户端列表的迭代器,语法格式为 Iterator it = clientList.iterator(); Iterator it = clientList.iterator(); while(it.hasNext()) { try { // 得到的Iterator别忘了强制转换回PrintWriter PrintWriter writer = (PrintWriter) it.next(); writer.println(message); writer.flush(); } catch (Exception e) { e.printStackTrace(); } } } } |
8. 多线程Socket编程的例子, 代码比较长, 放在下载里了, 链接如下:
http://files.cnblogs.com/notifier/SimpleChatroom.7z
我们可以通过调用 Automation Object Model 里的对象接口 ,来实现对
QTP 的操作,如打开 /运行脚本等动作
Automation object model 的对象结构图如下 (摘自QTP 参考手册 )
当程序进入死循环或者由于其他原因无法自行终止的时候,就需要强制退出程序了。
对于开发软件 Eclipse ,在程序执行超时后,可以点击 Terminate 按钮强制退出。
那么,我们可不可以通过程序设置一定的时间,当程序运行超过该时长后自行终止或者进行其他操作呢?
查了大量资料后发现,Future类就能满足这个需求。
Future类中重要方法包括get()和cancel()。
get()获取数据对象,如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。
另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。
通过这些方法即可实现我们要求。
Java 代码示例:
final ExecutorService exec = Executors.newFixedThreadPool(1); Callable call = new Callable() { public String call() throws Exception { // 放入耗时操作代码块 int cash = 300; String name = "张三"; System.out.println(name + "现在有" + cash + "元存款"); User u = new User(name, cash); String[] arr = { "线程A", "线程B", "线程C", "线程D", "线程E", "线程F", "线程G", "线程H", "线程I", "线程J" }; for (int i = 0; i < 10; i++) { MyThread th = new MyThread(arr[i], u, (int) (Math.random() * 1000 - 500)); th.start(); } //耗时代码块结束 Thread.sleep(1000 * 5); return "线程执行完成"; } }; try { Future future = exec.submit(call); String obj = future.get(1000 * 1, TimeUnit.MILLISECONDS); // 任务处理超时时间设为1 秒 System.out.println("任务成功返回:" + obj); } catch (TimeoutException ex) { System.out.println("处理超时啦...."); System.exit(0); } catch (Exception e) { System.out.println("处理失败."); e.printStackTrace(); } exec.shutdown(); // 关闭线程池 将耗时的代码块放入标注的地方后,即可满足要求。 System.out.println("处理失败."); e.printStackTrace(); System.out.println("处理失败."); e.printStackTrace(); |
在该示例程序中,当运行超时后,执行的是退出程序的操作。
也可以根据需要放入其他代码进行相关操作。
例如可以设置当处理超时时就忽略 该错误继续向下执行
1.首先查用户被锁时间:sql>select username,account_status,lock_date from dba_users where username='SA';
2.解锁:sql>alter user SA account unlock;
3.修改容许错误连接次数未无限大:sql>alter profile default limit FAILED_LOGIN_ATTEMPTS UNLIMITED;
ps:疑点是修改完密码后,有其他的人还在用错误的密码登陆导致超过了容错次数,导致SA被锁。
4.可能是最终原因:11G的oracle密码过期。
解决方法:
|-1、查看用户的proifle是哪个,一般是default:
sql>SELECT username,PROFILE FROM dba_users;
|-2、查看指定概要文件(如default)的密码有效期设置:
sql> select * from dba_profiles s where s.profile='DEFAULT' and resource_name='PASSWORD_LIFE_TIME';
|-3、将密码有效期由默认的180天修改成“无限制”:
sql> alter profile default limit PASSWORD_LIFE_TIME UNLIMITED;
5. 如果密码已经过期的,尝试用 alter user user_name identified by password account unlock;
操作WORD配置说明
引入:Word的对象库文件“MSWORD.OLB”(word 2000为MSWORD9.OLB)
1.运行Dcomcnfg.exe
2.组件服务――计算机――我的电脑――DCOM配置――找到microsoft word 文档
3.点击属性
4.选择“安全性”
5.选定“使用自定义访问权限”和“使用自定义启动权限”
6.分别编辑权限,添加Everyone(ASPNET,VS Developers,Debugger User)
7.选择“身份标识”,在选定“交互式用户” 即可
8.在
Web.config里加 identity impersonate="true"/
C#:
ASP.NET操作Word文档一直是一个大家比较关心的话题,其实在ASP.NET里操作Word文档一点也不难,大家只需按本文提示,就能轻轻松松操作Word文档!
首先请确认服务端已经安装了Office Word(以下将以Office XP为例),
操作系统为win2000或XP,并且已配置好.NET的运行环境及安装VS.NET C#开发环境后,我们就可以打开VS.NET,并新建一个Visual C#项目ASP.NET Web应用程序,位置为“”。(如图一)
二、引用Word对象库文件
要操作Word,我们就需要Word的对象库文件“MSWORD.OLB”(word 2000为MSWORD9.OLB),通常安装了Office Word后,你就可以在office安装目录的Office10文件夹下面找到这个文件,当我们将这个文件引入到项目后,我们就可以在源码中使用各种操作函数来操作Word。具体做法是打开菜单栏中的项目添加引用浏览,在打开的“选择组件”对话框中找到MSWORD.OLB后按确定即可引入此对象库文件,vs.net将会自动将库文件转化为DLL组件,这样我们只要在源码中创建该组件对象即可达到操作Word的目的!
三、Webform1.aspx.cs代码
完成添加引用后,MSWORD.OLB已经转化为相关DLL文件并放置于项目的BIN目录下了,这样我们只需在源码中创建该对象,并使用word库文件内置的操作函数即可轻松实现操作Word,Webform1.aspx.cs源码请参见
五、web.config设置
web.config文件还需添加一句 identity impersonate="true"/以启用模拟身份,因为默认ASPNET这个用户是没有权限访问Word.ApplicationClass(),当启用模拟身份后所有页面将会使用匿名Internet用户帐户(IUSR_machinename)这个用户名的权限执行,这样我们就能成功访问Word.ApplicationClass()并在ASP.NET中操作Word!
//传文档所在路径 返回文档内容 public string Doc2Text(string docFileName) { //实例化COM Microsoft.Office.Interop.Word.ApplicationClass wordApp = new Microsoft.Office.Interop.Word.ApplicationClass(); object fileobj = docFileName; object nullobj = System.Reflection.Missing.Value; //打开指定文件(不同版本的COM参数个数有差异,一般而言除第一个外都用nullobj就行了) Microsoft.Office.Interop.Word.Document doc = wordApp.Documents.Open(ref fileobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj ); //取得doc文件中的文本 string outText = doc.Content.Text; //关闭文件 doc.Close(ref nullobj, ref nullobj, ref nullobj); //关闭COM wordApp.Quit(ref nullobj, ref nullobj, ref nullobj); //返回 return outText; } |
当然 在读取的时候会有损坏的文件 和被加密的文件等问题 总之C#和office的兼容性不太好
别忘了要引用word的dll
引用文件夹 右键添加引用 在组件里找Microsoft.Office.Interop.Word
专门设计了一系列基本机制:
- 具有特权级别的处理器状态,能在不同特权级运行的各种特权指令
- 硬件机制使得OS可以和普通程序隔离, 实现保护和控制
处理器由运算器、控制器、一系列的寄存器以及高速缓存构成
运算器实现指令中的算术和逻辑运算,是计算机计算的核心
控制器负责控制程序运行的流程,包括取指令、维护CPU状态、CPU与内存的交互等等
处理器中的寄存器
寄存器提供了一定的存储能力,速度比主存快得多,造价高,容量一般都很小
两类寄存器:
用户可见寄存器,高级语言编译器通过算法分配并使用之,以减少程序访问内存次数
控制和状态寄存器,用于控制处理器的操作
由OS的特权代码使用, 以控制其他程序的执行
用户可见寄存器
机器语言直接引用
包括数据寄存器、地址寄存器以及条件码寄存器
数据寄存器(data register)又称通用寄存器
主要用于各种算术逻辑指令和访存指令
地址寄存器(address register)用于存储数据及指令的物理地址、线性地址或者有效地址,用于某种特定方式的寻址。如index register、segment pointer、stack pointer
条件码寄存器保存CPU操作结果的各种标记位, 如算术运算产生的溢出、符号等等
控制和状态寄存器
用于控制处理器的操作
大部分对于用户是不可见的
一部分可以在某种特权模式(由OS使用)下访问
常见的控制和状态寄存器:
程序计数器(PC:Program Counter),记录将要取出的指令的地址
指令寄存器(IR:Instruction Register),包含最近取出的指令
程序状态字(PSW:Program Status Word),记录处理器的运行模式信息等等
2.特权指令和非特权指令
使用多道程序设计技术的计算机指令系统必须要区分为特权指令和非特权指令
特权指令一般引起处理器状态的切换
处理器通过特殊的机制将处理器状态切换到操作系统运行的特权状态(管态)
然后将处理权移交给操作系统中的一段特殊代码,这一个过程称为陷入
CPU如何知道当前运行的是操作系统还是一般应用软件?有赖于处理器状态的标识
3.处理器的状态
根据运行程序对资源和机器指令的使用权限将处理器设置为不同状态——程序状态字PSW
多数系统将处理器工作状态划分为管态和目态
管态:操作系统管理程序运行的状态,较高的特权级别,又称为特权态(特态)、核心态、系统态
目态:用户程序运行时的状态,较低的特权级别,又称为普通态(普态)、用户态
具体处理器将CPU状态划分为两种、三种或四种
4.程序状态字PSW (Program Status Word )
在PSW中专门设置一位,根据运行程序使用指令的权限而设置CPU状态
CPU的工作状态码——指明管态还是目态,用来说明当前在CPU上执行的是操作系统还是一般用户,从而决定其是否可以使用特权指令或拥有其他的特殊权力
条件码——反映指令执行后的结果特征
中断屏蔽码——指出是否允许中断
CPU状态的转换
目态→管态
唯一途径 是 中断(广义)
管态→目态
设置PSW(修改程序状态字) 可实现
一条特殊的指令:访管指令
供用户调用操作系统的功能(服务)
INT,TRAP,SYSCALL
相关文章:
要点
DOM的简单介绍
DOM在QTP中使用的时机
DOM对象与IE对象模型的结合应用
DOM在Web测试中的具体应用
DOM在Web测试中的显著优势
1.DOM(Document Object Model)
DOM是Document Object Model文档对象模型的缩写。根据W3C DOM规范,DOM是一种与浏览器,平台,语言无关的接口,使得你可以访问页面其他的标准组件。简单理解,DOM解决了Netscape的Javascript和 Microsoft的Jscript之间的冲突,给予web设计师和开发者一个标准的方法,让他们来访问他们站点中的数据、脚本和表现层对像。
DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中导航寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而 DOM 被认为是基于树或基于对象的。
HTML DOM是HTML Document Object Model(文档对象模型)的缩写,HTML DOM则是专门适用与HTML/XHTML的文档对象模型。熟悉软件开发的人员可以将HTML DOM理解为网页的API。它将网页中的各个元素都看作一个个对象,从而使网页中的元素也可以被计算机语言获取或者编辑。 例如Javascript就可以利用HTML DOM动态的修改网页。
2.DOM在QTP中使用的时机
在使用QTP测试Web页面时,首先需要加载Web插件,随后QTP就可以顺利地识别一些标准的控件,但有些时候网页中存在一些特殊控件或者存在大量的相同控件时,可以尝试使用DOM的方式来进行控制,因为QTP只对一些标准的控件支持比较好,而有些特殊的控件QTP无法识别,导致无法对其进行操作。DOM是一种最底层的对象操作模型,使用它来控制对象不但速度快,而且可以访问很多QTP本身无法访问的东西。
(1)修改控件自身接口
此方法其实已经在第一章里详细讲解过它的应用,原理就是调用了DOM对象接口来修改控件的自身接口属性,这也是QTP本身所无法做到的。在实际测试过程也是一个非常有用的技术,关键时刻可以使问题迎刃而解。
(2)DOM对象下CurrentStyle对象应用
CurrentStyle 是一个可以与HTML 对象元素的style sheets进行交互的接口,它可以获取对象元素的字体名、字体大小、颜色、是否可见等。在Web测试中真对一些特殊的界面验证点时能够发挥出很大的作用。
(3)性能提升
对于性能来说,DOM的执行速度会比QTP的对象库执行速度快上好几倍,这是因为DOM相当于底层的对象接口,而QTP首先需要把对象属性进行封装,然后在脚本运行时调用对象库中的对象,最后与页面上的对象进行比对,如果属性匹配才可控制测试对象。而DOM却是直接找对象进行控制。所以,性能上相对于QTP的对象库有很大的提升,不过此优势一般只有在大量的相同对象或者一些特殊情况的时候才能有明显的区别。
注意:使用DOM时也需要注意一点,虽然DOM有很多优势,但是也不要过分依赖DOM,对象库才是QTP的核心,过分使用DOM会导致脚本维护方面相对比较繁琐,毕竟对象库维护起来是最方便的。
(1)修改控件自身接口
此方法其实已经在第一章里详细讲解过它的应用,原理就是调用了DOM对象接口来修改控件的自身接口属性,这也是QTP本身所无法做到的。在实际测试过程也是一个非常有用的技术,关键时刻可以使问题迎刃而解。
(2)DOM对象下CurrentStyle对象应用
CurrentStyle 是一个可以与HTML 对象元素的style sheets进行交互的接口,它可以获取对象元素的字体名、字体大小、颜色、是否可见等。在Web测试中真对一些特殊的界面验证点时能够发挥出很大的作用。在后续的章节中会详细对其进行分析讲解。
(3)性能提升
对于性能来说,DOM的执行速度会比QTP的对象库执行速度快上好几倍,这是因为DOM相当于底层的对象接口,而QTP首先需要把对象属性进行封装,然后在脚本运行时调用对象库中的对象,最后与页面上的对象进行比对,如果属性匹配才可控制测试对象。而DOM却是直接找对象进行控制。所以,性能上相对于QTP的对象库有很大的提升,不过此优势一般只有在大量的相同对象或者一些特殊情况的时候才能有明显的区别,这个也会在后续章节详细进行讲解。
3.DOM对象与IE对象模型的结合应用
(1)利用DOM操作测试对象
'使用IE COM启动IE Set oIE=CreateObject("InternetExplorer.Application") oIE.Visible=True '设置可见 oIE.Navigate "http://www.baidu.com" '跳转URL '等待IE页面加载完毕 While oIE.Busy Wend '获取Document对象 Set oDoc=oIE.Document '使用DOM对测试对象进行操作 With oDoc '输入框输入 .getElementByID("kw").value="谷歌" '点击搜索按钮 .getElementByID("su").Click End With Set oDoc=Nothing Set oIE=Nothing |
上面这个例子是通过getElementByID方法获取定位对象,除此之外还能通过getElementByName和getElementByTagName方法来获取定位对象。
(2)利用FORM名来获取对象元素
'使用IE COM启动IE Set oIE=CreateObject("InternetExplorer.Application") oIE.Visible=True '设置可见 oIE.Navigate http://www.baidu.com '跳转URL '等待IE页面加载完毕 While oIE.Busy Wend '获取Document对象 Set oDoc=oIE.Document '获取FORM名为f下名为wd的元素并输入 oDoc.f.wd.value="谷歌" oDoc.f.su.Click Set oDoc=Nothing Set oIE=Nothing (3)访问Web页面的Script脚本变量 '使用IE COM启动IE Set oIE=CreateObject("InternetExplorer.Application") oIE.Visible=True '设置可见 oIE.Navigate http://www.baidu.com '跳转URL '等待IE页面加载完毕 While oIE.Busy Wend '获取Document对象 Set oDoc=oIE.Document '获取搜索框 Set oEdit=oDoc.parentWindow.k '并对其进行输入 oEdit.value="谷歌" '获取FORM名为f下名为su的元素并点击 oDoc.f.su.Click Set oDoc=Nothing Set oIE=Nothing |
这一部分主要介绍了利用IE的COM以及HTML DOM来自动化IE浏览器,以及对浏览器的一些控件对象进行自动化的操作,包括启动IE、等待页面加载、遍历所有IE窗口、利用DOM操作测试对象、利用FORM名来获取对象元素、访问Web页面的Script脚本变量、Browser对象转化Window窗口对象、自定义浏览器应用程序,这些方法对于我们在自动化测试中也是起到比较重要的作用,并且能够辅助我们更好地完成Web自动化测试,当QTP不能达到我们想要达到的目的时,就可以使用这些方法来代替或者说来实现需要实现的方法,最终使Web自动化测试变得更加的轻松和容易。
4.DOM在WEB测试中的应用
当编写QTP脚本时,首先该做的就是将需要控制的测试对象添加到对象库,添加完毕后即可使用QTP的封装方法来控制测试对象。
如果需要在QTP中访问DOM,就只需要使用Page对象,并调用Page对象的Object封装属性,QTP就能访问到顶层DOM对象。
QTP中访问DOM对象的方法:
Browser("micClass:=Browser").Page("micClass:=Page").Object
此处的Object属性目前只支持IE,通过DOM,可以在QTP中修改HTML元素对象的属性,调用其方法,当在使用Web测试对象的Object属性时,事实上就已经自动获取到DOM对象的一个引用,这就意味着可以调用测试对象的DOM引用下的所有的属性、方法。
实例1:以百度首页为例
Set oDocument=Browser("micClass:=Browser").Page("micClass:=Page").Object oDocument.getElementByID("kw").value="谷歌" oDocument.getElementByID("su").Click 实例2:以百度高级搜索为例,www.baidu.com/gaoji/advanced.html 'QTP中使用DOM控制各类HTML元素 '获取DOM对象 Set oDocument=Browser("micClass:=Browser").Page("micClass:=Page").Object Set allWebEdits=oDocument.getElementsByTagName("INPUT") For each oWebEdit in allWebEdits oWebEdit.value="谷歌" Next |
5.DOM在WEB测试中的显著优势
利用DOM完成QTP无法完成的任务
使用CurrentStyle验证对象
HTML源码:
<style> .class_visible{visibility:"visible"} .class_hidden{visibility:"hidden"} </style> <div class=class_hidden id="ID_001"> <p>DHTML using DISPLAY</p> </div> QTP中代码: Set oElementDocument=Browser("micClass:=Browser").Page("micClass:=Page").WebElement("html id:=ID_001").Object isVisible=oElementDocument.currentstyle.visibility If isVisible="hidden" Then msgbox "object is hidden" Else msgbox "object is visible" End If |
此处如果用QTP的Exist方法,结果永远返回True。因为此对象的确是存在于网页中,但是被设置了不可见,而Exist方法只能验证对象是否存在,却不能验证是否隐藏。
而Document对象下的currentstyle可以直接访问style sheets。
利用DOM提升性能
当对象较多时,使用DOM较为占优势,数量越多越明显,比如有1000个文本框的HTML页面,每个文本框的name属性都由text_开头,之后由1到1000递增,脚本如下:
<html> <head> <script language="vbscript"> function msg for i=1 to 1000 tt=tt+"<input type='text' name='text_"+cstr(i)+"'>" next Document.getElementByID("aaa").innerHTML=tt end function </script> </head> <body> <input type="button" value="click it" onclick="msg"> <div id="aaa"></div> </body> </html> |
把以上脚本保存成HTML,打开此HTML,点击按钮生成1000个文本输入框。比较QTP描述性编程和DOM操作脚本的性能。
QTP描述性编程脚本:
Services.StartTransaction "inputvalue" For i=1 to 1000 Browser("micClass:=Browser").Page("micClass:=Page").WebEdit("name:=text_"+cstr(i)).Set "value"+cstr(i) Next Services.EndTransaction "inputvalue" |
结果:
DOM操作脚本:
Services.StartTransaction "inputvalue" For i=1 to 1000 Browser("micClass:=Browser").Page("micClass:=Page").Object.getElementsByName("text_"+cstr(i))(0).value="value"+cstr(i) Next Services.EndTransaction "inputvalue" |
结果:
刚在做内网内某个项目的性能测试
项目部署的端口为4000
然后用Microsoft Web Application Stress Tool 测试 发现居然不支持非80端口 找遍整个配置界面没发现port选项
寻思着会不会是写到配置文件里呢 于是打开安装目录寻找 苍天不负有心人 居然找到了WAS.mdb 这个文件
停掉webtool服务 怀着期待的心情打开
好多好多的表 然后就一段瞎点
终于看到了这个东东
看到了吧 port了吧 接下来该干嘛 你懂得
挺不明白微软的想法。。。 居然直接默认值就设置为80 还不提供修改的选项
修改完后 记得保存 关掉该access 文件 重启服务
然后 然后就让它开始飙起来吧
首先,需要将UNITILS相关的jar包导入到系统的依赖jar包文件lib中,相关的jar文件如下:
dbunit-2.4.8.jar hamcrest-all-1.3.0RC2.jar hamcrest-library-1.1.jar htmlunit-2.8.jar htmlunit-core-js-2.8.jar httpclient-4.0.2.jar httpcore-4.0.1.jar httpmime-4.0.1.jar junit-4.8.2.jar mockito-all-1.8.5.jar nekohtml-1.9.14.jar operadriver-v0.6.jar selaid-1.0.1.jar selenium-java-2.4.0.jar spring-ws-test-0.22.jar testng-5.14.4.jar unitils-core-3.1.jar unitils-database-3.1.jar unitils-dbmaintainer-3.1.jar unitils-dbunit-3.1.jar unitils-mock-3.1.jar unitils-orm-3.1.jar unitils-spring-3.1.jar unitils-testng-3.1.jar xmlunit-1.2.jar xmlunit-1.3.jar |
此外,除了常见的jar包之外,还有一个commons-lang包也是需要的,具体报错的时候,在网上找一下就ok了.
然后,使用Eclipse的new功能,创建一个基于Junit4的
test case,需要注意的事情是,需要让该test case能够得到spring 的SpringApplicationContext对象,得到了Spring的SpringApplicationContext对象之后,后面的
测试用例就比较简单了.在获取SpringApplicationContext的时候,一定要注意注解里面的文件位置的获取.我们可以多看一下编译之后的文件,不要仅仅依据eclipse的文件的位置来判断相对位置.下面是我的Test Case的父类,以及子测试类:
package com.ziwen.common; import org.springframework.context.ApplicationContext; import org.unitils.UnitilsJUnit4; import org.unitils.spring.annotation.SpringApplicationContext; public class CommonTest extends UnitilsJUnit4{ @SpringApplicationContext({ "springConfig/applicationContext.xml", "springConfig/bizContext.xml", "springConfig/daoContext.xml"}) private ApplicationContext applicationContext; public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } } 子类: package com.ziwen.biz; import static org.junit.Assert.assertNotNull; public class TestDemoBiz extends CommonTest{ @SpringBean("demoBiz") private DemoBiz demobiz; @Test public void testDoSth() { List<Demo> list=demobiz.getDemoList(new Page(10)); assertTrue(list.size()>1); } @Test public void testGetDemoList() { String str=demobiz.doSth(); assertNotNull(str); } } |
测试的结果: