qileilove

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

可用性测试

 如今的ICT解决方案的复杂性正在增加,由于位于多个地点并由不同方来管理的集成系统的存在。而他们常常部分由云管理的事实使得事情变得更加复杂。因为组织提供24/7的企业对企业的服务,这些集成解决方案的可用性也变得越来越重要。
  在互联网上,你会发现数百个销售同种产品的网店。万一不可用,客户就很容易切换到另一家店。
  因此,一个解决方案的可用性对业务至关重要。大多数情况下,在生产中监测可用性,如果服务不可用就采取改进措施。防止被看作是这种质量特性的业务指标的可用性问题是有必要的。
  这篇文章介绍了可用性测试使用的测试设计技术:措施可用性的“状态转换测试” ( STT )。
  
  状态转换测试
  最正式的测试设计技术是基于工艺流程或数据的(根据可能的输入或设计技巧划分,因为他们检测不同的问题。)所以经常去试着用工艺流程导向和数据输出导向的设计技术的组合。
  状态转换测试设计技术的强大之处在于它是基于机器状态的,因此,它不同于大多数正式的测试设计技术。
  
  可用性
  在ISO 25010里 ,可用性被定义为: “当需要用到时,一个软件组件可操作和可使用的程度” 。
  它还提到,可用性可以由软件产品处于升级状态时的总时间比例来外部评估。因此可用性是成熟(控制故障率),容错性及可复原性(控制每次故障后停机时间的长度)的组合。
  大多数解决方案可用性的相关问题是由解决方案运行上的基础设施事件造成的。每个人都至少可以给出一个他或她由此事件造成的故障的亲身体验的例子,例如:电源故障或从互联网断开。这类故障的影响普遍很大。
  然而,由于它们主要涉及基础设施(不在项范围之内),相关业务风险往往在软件开发项目中没有确定且没有被测试。
  
  开发测试
  负责解决方案“业务管理”或“开发”的部门是“开发测试”的利益相关者。
  开发测试是基于荷兰术语“Exploitatie testen ” 。这不是最终的翻译,但它是最恰当的。
  也可以翻作 “业务就绪测试”,但这只覆盖ITIL /服务管理的业务部分,所以,不匹配。“生产验收测试”也是一种翻译,但在我看来,它更关注生产环境的验收。
  因此,我把 “Exploitatie testen” 翻译为“开发测试” 。
  开发测试的定义:
  检查是否关于应用程序和底层IT基础架构的同意或预期的服务水平可以实现。
  这些协议和/或期望在一个所谓的服务水平协议(SLA )的合同是正式的。
  一个SLA的定义:
  一方为客户另一方为服务提供商的双方协议。
  SLA描述了IT服务,文件服务水平目标,并详细说明了IT服务提供商和客户的责任。
  SLA中对解决方案可用性的相关要求进行了描述。
  图1显示了开发测试在V模型中的位置。

图1.开发测试在V模型中的位置

  (当然)这个过程业务需求的收集。

  该系统的规格是基于功能和一些非功能的需求。一些业务要求(例如可用性和安全性需求)也将影响与IT服务提供商的合同( SLA)。
  测试管理技术“风险管理”通过识别并优先考虑关于IT服务管理的业务风险提高了这一过程。
  SLA中的利益相关者是:
  ▪功能管理
  ▪审计员
  ▪安全员
  ▪财务管理
  ▪技术管理
  ▪服务水平管理(业主)
  ▪业务
  IT服务水平协议也会影响系统的规格。
  没有各方的参与不能达成协议。

因此,SLA将在UCS和OLA变得有形。这些合同也将影响系统规范。
  例如,3秒的最大响应时间的要求仅通过基础设施不能实现。也需要性能优化的软件去满足这一要求。
  在V模型中,开发测试被描述为一个不同的测试水平。
  开发测试将基于SLA (测试基准)上,并由IT服务管理的组织执行。
  业务可能为了接受所提供的IT服务,执行不同的开发测试(开发验收测试) 。
  表1展示了:执行以检查是否服务供应商能够提供与SLA中所描述一致的议定质量的测试。

表1.被执行的测试

  第一列显示了SLA项,第二列显示方法/技术,它可用于以检查是否可以满足需求。
  SLA项被描述为一个ISO 25010质量属性(ISO 25010质量属性能让测试人员理解SLA项并可选择一个测试技术来测试SLA项)。
  可维护性可以在一个静态的方式通过审查的规范和源代码的完整性(可用性版本历史记录,版本说明等)和可理解性(可用性的设计决策,等等)进行测试。
  它也可以通过在其他测试的执行过程中监视和分析解决时间来被隐式地检查。
  为了能够执行这些检查,支持组织必须尽可能的逼真。
  该系统的安全性,也可以通过审查规格和源代码来检查,例如在外部通信使用加密检查,使用双因授权方法及其他安全协议。它也可通过“渗透测试”来动态测试。
  还有很多工具都可以通过执行上千称为“黑客”的尝试来检查你的系统的脆弱性。
  很好的例子是工具Nessus和AppScan。该组织OWASP (开放式Web应用程序安全项目),其重点是提高软件的安全性,且通过其网站www.owasp.org)提供关于安全性的有用信息
  系统功能相关或系统效率相关的测试更广为人知。
  已有不少关于这一主题的文章了。
  因此,本文的重点不是测试该项。
  系统可用性和可靠性相关的SLA项(例如,在系统的正常运行时间和过程中无丢失数据)可以通过使用状态转换测试技术来测试。
  
  使用状态转换测试技术来测试可用性
  下面你可以看到用在测试用例中测试系统可用性的步骤。
  测试规范步骤:
  1 。详述系统组件影响系统的可用性
  首先必须识别:可能影响系统可用性的不同(基础的)组件。这可以通过使用一种风险管理技术来完成。组件例子是路由器和服务器
  2 。详述可能会发生的故障
  下一步是定义可能发生在该组件的故障。
  风险识别技术,如使用鱼骨图可以帮助识别可能出现的故障。
  3 。详述用来防止这些故障的措施
  可以采取不同的措施防止这些故障,如负载平衡(防止因负载峰值造成的停机)和故障转移机制(防止由不可用的服务器造成的系统停机期) 。
  这一步也可能导致额外的识别措施(防止尚未被识别的风险)。
  4 。准备状态转换图
  状态转换图可以基于在上一步中相熟的措施来准备。这个步骤可以分为两个子步骤:
  A.定义关于这些措施的状态
  措施根据不同的系统状态做出反应。当一个有效的服务器失效了,故障转移机制将被激活。这些不同的状态需要在这一步中定义。
  B.可视化之间的状态和转换
  在第二个子步骤中,需要可视化这些状态之间的关系(一个状态转换图内)。
  在这儿定义导致从一个状态过渡到另一种的触发器。
  5 。详述测试用例
  状态转换图完成后,可详述测试用例。
  下一个例子可以明确使用状态转换测试以测试可用性。
  1 。详述系统组件影响系统可用性
  在一个公司的基础架构里,对系统的可用性至关重要的应用程序和数据库服务器都确定了。

表2.测试用例

  2。详述可能发生的故障
  这些服务器可能是由于交流电源中断而不可用。
  3。详述为防止这些故障采取的措施
  为防止这种电源中断的度量可能是引进的不间断电源(UPS)。
  4。准备状态转换图
  a.定义关于这些措施的状态
  一个简单的UPS可以有四种状态:
  I.基本电源(或待机模式),其中该系统有正常的交流电源。
  II.开,其中,所述交流电源是关闭的或低的。
  III.关,其中UPS电源已经用完了。
  IV.充电,在UP使用可用交流电源充电。
  b.准备状态转移图
  下面是属于这个UPS的状态转换图:

  图2。状态转换图




  5。详述测试用例
  最后的步骤是详述基于所述状态转换测试的测试用例。如BS7925-2中所述,状态转换测试中的完整性水平可以因使用的不同级别switch覆盖率而变化。在这个例子中,用了一个0-switch覆盖,即1个记录所有有效序列进行了测试。表2展示了测试用例。

posted @ 2014-05-19 10:08 顺其自然EVO 阅读(156) | 评论 (0)编辑 收藏

zTree从数据库中动态加载树形菜单

这几天做动态菜单用到了这个插件,目前用的很广泛的一个开源框架,最新发布的QUI框架就是用这个插件开发的菜单部分,因此还是很值得深入研究和学习,通过使用感觉功能很丰富,好多函数不用自己开发和编写,官网上有很详尽的API可以参考,用着算顺手但学习使用的过程中也遇到了一些困难,听过反复测试和查资料都理解了,但也在思考一个问题,怎么样才能使得最快的时间从接触一个新东西到灵活掌握的程度?
  这个不仅仅是一个树形结构的菜单,每个节点左边可以有一个复选框,看了看也挺简单的,只需要在setting里面配置一个checked属性即可。
  目前经验觉得这个用在组织结构、分类、尤其是权限,如果用这个插件完成会很完美和自己的业务逻辑相结合。
  原理是很容易理解就不过多说它的原理了,大致同ajax异步请求原理相同,看一遍介绍你也就明白了,这个框架全部是异步请求数据,提高了用户体验度。
  在学这个过程中,本人有如下几点浪费了点时间拿出来和大家分享一下,以便大家下次项目中使用更容易上手。
  静态数据与动态数据
  这个框架支持XM、json等多种数据格式,建议大家使用json格式数据觉得效果好一些、加载的时候快一些,关于数据格式可以参考官网给的一些数据,官网上面给的都是一些假数据,如果动态生成菜单淡然需要从数据库里面查询出来,然后转换成json字符串了,需要自己解析json字符串。
  在之前做项目中还真没有自己好好研究一下解析字符串,现在用到了只能现学现弄这样减慢了开发效率,这应该属于开发基础。
  我自己试着写几种方法解析、试着引用网上的方法、几种迭代都不能生成正确的字符串,最后一种还是解析出来了,从数据库查询出来的是list列表,然后把他解析成了一个json串,所有数据都显示在顶层菜单,检查json串和给的例子是一样的但是还是出不来折叠效果。
  原来并不是解析json串不对,而是它打印到界面上是一级一级打印,并不是一下全部查出来都打印出去,这样当然就在一个级别上了,这也是所说的静态加载全部节点,很让人恶心的json串,强烈建议大家数量掌握几种解析json、array等以及相互转换的方法,这些是很基本的能力,平时会经常用的。
  isParent节点
  我们一看都知道这个节点表示是不是父节点,它有什么含义呢,在使用中我发isParent为true时,表示的是该节点左边接受单机事件,也就是会有一个展开符号,每次点击会触发一次异步提交数据,请求子节点数据加载到页面上。正常情况下如果你不点击父节点所有子节点是不加载到页面的。
  后台生成树json串代码
treeList=resourceService.list(childMap);
JSONArray jsonArray=new JSONArray();
for(Organization organization:treeList)
{
JSONObject jsonObject=new JSONObject();
jsonObject.put("id",organization.getId());
jsonObject.put("pid",organization.getPid());
jsonObject.put("name",organization.getName());
//判断所选择节点是否是父节点,如果是设置isParent属性为true,不是设置为false
Map subchildMap=new LinkedHashMap();
subchildMap.put("sqlid","SubChildLst");
subchildMap.put("id",organization.getId());
List<Organization> subtreeList=new ArrayList<Organization>();
subtreeList=resourceService.list(subchildMap);
if(subtreeList.size() > 0 )
{
jsonObject.put("isParent","true");
}else {
jsonObject.put("isParent","false");
}
boolean isChecked=resourceService.IsChecked(contactid,organization.getId());
if (null==contactid || "".equals(contactid) || "null".equals(contactid)) {
jsonObject.put("checked","false");
}else {
if (isChecked) {
jsonObject.put("checked","true");
}else {
jsonObject.put("checked","false");
}
}
jsonObject.put("open","false");
jsonArray.put(jsonObject);
}
String json=JsonUtil.toJson(jsonArray)
 checked节点
  该树形是选中的节点,这个节点也很有用,比如加载一个商品它是属于哪些分类的,在加载树的过程中,要把默认选中的项目加载上来,这个你想怎么实现呢。
  动态加载默认选中的节点,用了半天的时间才想出怎么弄,有时候并不是我们不会写代码而是没有思路,有时有思路但是行不通这时就需要我们转换思考角度,在编程中也要注意从多角度思考,不要钻到一个点上去。
  解决动态加载默认选中项我用的是传递参数,在一般的页面上面传递参数觉得很容易,我要用的这个页面是一个弹出页面,使用的是window.open属性,在弹出框上动态加载菜单并把选中的选中,ztree从官网上看API说是不能够传递参数,有一个otherparm属性可说是只接受静态参数,是一个一个的键值对,我在value处又加了一个js函数,通过这个函数调用父窗体上的一个变量的值,代码如下;
<SCRIPT type="text/javascript">
var setting = {
check: {
enable: true,
chkStyle: "checkbox",
chkboxType : { "Y" : "", "N" : "" }
},
//获取json数据
async : {
enable : true,
url : "http://127.0.0.1:8080/contact/resource.do?method=getzTreeNodes", // Ajax 获取数据的 URL 地址
autoParam : [ "id", "name" ], //ajax提交的时候,传的是id值
otherParam: ["contactid",function(){
return window.opener.document.getElementById("contactid").value;
}]
},
data:{ // 必须使用data
simpleData : {
enable : true,
idKey : "id", // id编号命名
pIdKey : "pId", // 父id编号命名
rootId : 0
}
},
// 回调函数
callback : {
onClick : function(event, treeId, treeNode, clickFlag) {
if(true) {
alert(" 节点id是:" + treeNode.id + ", 节点文本是:" + treeNode.name);
}
},
//捕获异步加载出现异常错误的事件回调函数 和 成功的回调函数
onAsyncSuccess : function(event, treeId, treeNode, msg){
//  alert("调用成功!");
//var nodes=getCheckedNodes(true));
//alert(nodes);
},
beforeClick: beforeClick,
onCheck: onCheck
}
};
function beforeClick(treeId, treeNode) {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.checkNode(treeNode, !treeNode.checked, null, true);
return false;
}
var code;
function showCode(str) {
if (!code) code = $("#code");
code.empty();
code.append("<li>"+str+"</li>");
}
$(document).ready(function(){
$.fn.zTree.init($("#treeDemo"), setting);
//setCheck();
});
function onCheck(e,treeId,treeNode)
{
var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
nodes = zTree.getCheckedNodes(true),
v = "";
var ids="";
for (var i=0, l=nodes.length; i<l; i++) {
v += nodes[i].name + ",";
ids+=nodes[i].id+",";
}
if (ids.length > 0 ) ids = ids.substring(0, ids.length-1);
alert(ids);
if (v.length > 0 ) v = v.substring(0, v.length-1);
cityObjIds=window.opener.document.getElementById("cateSelIds").value=ids;
cityObjName=window.opener.document.getElementById("cateSelName").value=v;
}
function getSelectedNodes()
{
var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
nodes = zTree.getCheckedNodes(true),
v = "";
var ids="";
for (var i=0, l=nodes.length; i<l; i++) {
v += nodes[i].name + ",";
ids+=nodes[i].id+",";
}
if (ids.length > 0 ) ids = ids.substring(0, ids.length-1);
alert(ids);
if (v.length > 0 ) v = v.substring(0, v.length-1);
//var cityObj = $("#citySel");
//var cityObjIds = $("#citySelIds");
//给父窗体updateContact.jsp中所属分类赋值
window.opener.document.getElementById("cateSelIds").value=ids;
window.opener.document.getElementById("cateSelName").value=v;
}
function  winClose()
{
window.close();
}
lt;/SCRIPT>
 运行效果,动态下拉列表树
  在工作中别人给你讲解代码或者一点点详细的讲解几乎是没有的,我们那个经理只说你用那个ztree做一下那个动态菜单。
  作为一个程序员要能读懂别人代码
  让我看别人的代码,我说看着看着就不想看了,还不如我自己动手写呢,其实,看别人代码一直觉得挺没意思的,现在想法改变了些,别人写的代码不管是好是坏,我们都值得看一看借鉴借鉴,一直在提高班学习没有怎么看别人写的代码到底是怎么样的,觉得提高班人写代码无论质量如何,风格都是一样的
  注释多、空行多、格式规范,易读性很强,这就和学英语一样,只听标准音是不行的,需要挺标准音的同时也要听听方言,这样才能让我们的阅读代码的能力真正的提高。
  作为一个程序员或者开发人员有不合适的地方要主动解决,并去优化。
  前几天遇到了一个问题,需要通过一个父节点ID,拿到他下面的所有子节点列表或ID,在给我的代码中用存储过程实现的但用起来运行效率较慢,项目经理让我优化优化存储过程可以看懂但不知道该优化哪里,从网上找了找找到一个算法替换后果然查询变快了。
  说一下学习一个新东西的过程
  有时让你做一个东西往往会用到新东西,一个你没有接触过的东西,在这个时候我们怎么样才能最快的学会了并且把任务做出来。
  觉得在项目中该多思考、有时甚至可以不动手但是一定要多去思考,而不是别人告诉你怎么做怎么做,举一个最简单的例子post提交和get提交有什么区别?一个不起眼的问题如果你没有认真思考过,有时在某个时刻你会因此遇到一个小障碍,觉得这个跟解一道数学题一样,只有把每个知识点理解了、相互之间能联想到一起,达到各种知识综合灵活运用,做项目的过程中才谁能得心应手,也容易达到米老师所讲的最高境界。

posted @ 2014-05-16 11:09 顺其自然EVO 阅读(3393) | 评论 (1)编辑 收藏

Java定时器的使用

定时器类Timer在java.util包中。使用时,先实例化,然后使用实例的schedule(TimerTask task, long delay)方法,设定指定的任务task在指定的延迟delay后执行。定时器任务类TimerTask是抽象类,继承并重写其run()方法,可实现具体任务。
  schedule(TimerTask task, Date time)设定指定任务task在指定时间time执行。
  cancel()方法结束这个定时器。
  schedule(TimerTask task, long delay, long period)方法设定指定任务task在指定延迟delay后进行固定延迟peroid的执行。
  scheduleAtFixedRate(TimerTask task, long delay, long period)方法设定指定任务task在指定延迟delay后进行固定频率peroid的执行。
  要实现一个定时任务,运用java中的Timer和TimerTask类可以非常容易实现实时调用处理函数。这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需要。
  看个简单的例子:
import java.io.IOException;
import java.util.Timer;
public class TimerTest {
public static void main(String[] args){
Timer timer = new Timer();
timer.schedule(new MyTask(), 1000, 2000);//在1秒后执行此任务,每次间隔2秒,如果传递一个Data参数,就可以在某个固定的时间执行这个任务.
while(true){//这个是用来停止此任务的,否则就一直循环执行此任务了
try {
int ch = System.in.read();
if(ch-'c'==0){
timer.cancel();//使用这个方法退出任务
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class MyTask extends java.util.TimerTask{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("________");
}
}
}

posted @ 2014-05-16 11:08 顺其自然EVO 阅读(156) | 评论 (0)编辑 收藏

改写Selenium Web Driver Sample Code

  用Selenium WebDriver做网页自动化测试已经不少时间,一直用的语言的Python,最近突然很想学习Java,于是自己搭了一个Eclipse+Selenium 2.32的开发环境。
  Sample代码有一段如下,不是很好理解,
(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return d.getTitle().toLowerCase().startsWith("cheese!");
}
});
  我把它重写成了
WebDriverWait WDW = new WebDriverWait(driver, 10);
WDW.until(new theWait());
//(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
//    public Boolean apply(WebDriver d) {
//        return d.getTitle().toLowerCase().startsWith("cheese!");
//    }
//});
// Should see: "cheese! - Google Search"
public static class theWait implements ExpectedCondition<Boolean>{
public Boolean apply(WebDriver d) {
Boolean bool =  d.getTitle().toLowerCase().startsWith("cheese!");
if(bool == true)
{
String title = d.getTitle();
System.out.println("Page title is: " + title);
}
return bool;
}
}
  测试通过,可以做为一个Java函数override的小例子

posted @ 2014-05-16 11:06 顺其自然EVO 阅读(238) | 评论 (0)编辑 收藏

使用模拟器和实际设备进行自动化测试

    移动测试的挑战


  在看移动测试的自动化工具前,你需要对与移动应用程序面临的挑战稍作了解。

  设备

  移动应用程序应该在你要求的设备上工作。

  移动应用程序必须在每个设备上都正常工作。

  移动设备必须为应用程序的运行时间进行测试。

  移动设备处理能力不同,内存有限,还必须考虑通信协议。

  应用

  新的操作系统版本和功能意味着开发人员建立了必须被测试的新的,更复杂的程序。

  多个构建常常时间很短,因此脚本执行往往不能完成。

  网络

  多种网络类型,必须进行测试,如GSM,CDMA,GPRS和Wi-Fi。

  不同的连接速度(包括2G,3G和4G LTE)必须跨地点测试。

  世界各地有超过400多的移动网络运营商,测试必须处理各种网络。

  技术

  必须考虑大量的测试用例。

  必须处理手机特有的功能,包括触屏约定。

  必须执行API级别的测试。

  移动应用程序类型

  当制定移动测试策略时,你必须清楚了解可能需要测试的应用程序。移动应用程序可分为本地应用程序(Native App), 网络应用程序(Web App)和混合应用程序(Hybrid App)。

  Native应用程序

  Native App是专为移动操作系统所建,并直接安装到该设备上。

  用户通常通过网上商店或市场(如App Store)获取这些应用程序。

  Native App是用本地编程语言构建的。例如:iPhone或iPad apps是用ObjectiveC构建的,Android apps是用Java构建的。Native App速度快,提供更好的用户体验和界面,并且通常可以获取目标设备的所有功能。

  Native Apps的功能包括:

  存储。二进制“可执行映像”,被明确下载并存储到移动设备的文件系统中。安装过程可以由用户,或者在某些情况下,由企业的IT部门开启。

  分布。获得Native Apps最常见的方法是去有相关特定设备的应用程序商店或市场(iTunes有iPhone或iPad的apps,安卓市场有Android Apps),或者通过企业分配机制获取。

操作。程序直接在操作系统中运行:

  由主屏幕开启。

  不需要另一个存储器应用程序来运行它。

  明确利用操作系统APIs。

  移动Web应用程序

  移动Web应用程序是专门针对移动设备的网络驱动应用程序。

  这些应用程序是通过移动设备的网页浏览器获取的(例如:iPhone上的Safari)。用户不需要在设备上直接下载和安装该应用程序。







手机模拟器

费用: 手机模拟器是作为每个新的操作系统发布的软件开发工具包的一部分来免费提供的。

简单: 模拟器下载和安装简单,即刻使用。许多模拟器能够以简单和直接的方式来运行。

快速: 比起要连接到本地网络或云的真实设备,模拟器的等待时间更短。

硬件支持: 仿真模拟里,完全的硬件支持是无法测试的。

计算资源: 根据PC运行模拟器的处理能力和被用来测试的手机和智能机的类型,模拟器上的表现比起真实设备,可能是不切实际的好或坏。
网络互用性:使用模拟器不可能测试网络相关事件的影响 (如:来电,短信),不同的关于移动应用程序行为的网络技术(如: HSPDA, WCDMA, UMTS和LTE). 由于模拟器并没有连接到移动网络,它们不支持互操作性测试。

真实设备

可信赖的: 在真实设备上进行的测试给出的是最精确的结果。
网络的互操作性:真实设备测试是在真实的网络中进行的。用户体验:通过使用真实设备, 可以把特定设备的CPU,内存或屏幕大小等元素考虑在内,准确地看到用户体验。

物流和成本: 购买不同版本操作系统的不同设备是相当昂贵的,还要浪费不少精力来购买和管理这些设备。

  表1:使用模拟器和真实设备的利弊

  移动Web应用程序功能:

  完全使用Web技术,如HTML(尤其是HTML5),CSS,Javascript代码写的。

  该代码是由浏览器执行,而不是由操作系统。

  用户可以通过多种方式启动应用程序:输入网址,单击超链接,扫描QR码,或者单击主屏幕上的快捷方式。

  安装是非强制性的。

  支持多种操作系统。

  混合应用程序

  类似本地应用,混合应用程序是使用传统的Web技术开发的。

  Hybrid applications是在每台设备上的本地应用程序存储器中运行的,但却是集中部署和维护,是跨平台的性质的。通常情况下,他们是由云服务,所以地球上任何地方的终端用户体验是一致的,跨设备的。


    使用模拟器和实际设备进行自动化测试


  模拟器是用来复制一个移动设备的内部工作的。它是用于开发和测试移动应用程序的强大工具,被用于手动和自动化测试中。

  当然,移动APP是用在真实设备,而不是模拟器上的,所以测试必须在实际设备上进行,以确保应用质量的最高水平。

  然而,让你们组织里每个移动测试团队都拥有一个实际设备是很烧钱的,所以使用模拟器是一个可以控制成本的有效方法。

  在制定移动测试策略时,你们组织应该谨慎拳皇使用模拟器或实际设备的利弊。

  移动设备自动化测试工具分类

  有三种类型的工具可以支持移动设备的自动化测试。

  本地平台工具

  本地平台工具通常是由移动平台供应商提供的软件开发工具包的一部分。这些框架通常与用户界面对象级别的应用程序进行交互。

  这些工具允许更复杂的基于对象的交互,十分成熟,还支持本地UI对象,因为它们是平台供应商支持的。

  因为这些是操作系统级别的应用程序对象,你可以通过用测试中的应用程序编译的小数据库(也被称为 “instrumentation”)洞察他们。

  基于视觉的多平台工具

  基于视觉的多平台工具最常用在移动设备自动化测试里。

  这些工具通过可视化手段与设备交互,并可以识别文本或图像,使测试人员构建基于这些认识和内置的手势的自动脚本。

  基于视觉的多平台工具的优点是它们支持多个平台,并且可跨多个设备执行测试。

  视觉对象由OCR引擎(基本上都是将扫描的手写、机打图像,或印刷文本转换成机器编码的文本智能软件引擎)识别。

  基于对象的多平台工具

  基于对象的多平台工具可以在应用程序内通过识别,拦截,并发送信息到对象,直接用和传统的测试工具一样的方式来与应用程序UI对象进行交互。

  这些工具的优势是,他们支持多种平台,并且可以跨平台上执行测试。

  对象级整合也对应用程序变化更加宽容,从而降低与自动化测试相关的整体维护成本。

  移动自动化测试的方法

  在规划您的移动自动化工作时,别忘了以下的工具评估和选择,对象技术的方法:

  工具评估和选择

  执行工具的可行性,以检查是否该工具可以在各种移动技术和平台使用。

  选择一个同时支持真机和模拟器或仿真器的工具。

  识别多种设备和版本支持。

  用实用性和可重复使用功能增加自动化测试工作的价值。

  了解如果选择的工具需要你破解或获取设备的根。

  确保该工具支持操作系统的新版本。

  对象识别

  基于图像的对象识别:把每个测试对象记录为图像,在GUI中匹配对象和可用运行时间图像。

  光学字符识别对象的对象识别:使用光学字符识别(OCR)功能获取屏幕上的控件的文本。该功能使用了通过字符读取字符文本的专门算法。

  真实对象或本地的对象标识:标识唯一对象的属性,如“ ID”,“名称”和“类” 。

  基于DOM的识别:利用DOM属性来识别web应用程序对象。




特点

图像识别

OCR对象

本地对象

DOM对象

对象识别的复杂度

容易

容易

中等

中等

对象维护工作



容易

中等

跨设备支持


中等

容易

容易

执行中的识别速度

中等

中等

中等


  结论
  通过在移动应用程序测试中使用自动化测试,测试团队可以在保持质量和减少将产品推向市场时间的同时降低成本。
  许多工具可用来支持移动设备自动化测试。选择正确的工具需要理解业务需求和移动测试独有的因素。
  权衡手机模拟器和真实设备的优劣,企业的最佳移动测试解决方案往往不是只选择其中一个,而是选择结合这两者。

posted @ 2014-05-16 11:04 顺其自然EVO 阅读(243) | 评论 (0)编辑 收藏

IOS 多语言自动化测试 - 如何自动切换运行语言环境

 最近一直在研究IOS在多语言环境下的自动化测试,其中一个重大的问题就是如何在自动化测试的时候能够自动切换语言,
  比如某个软件支持10个国家的语言,如果不能自动的切换语言,那么在测试的过程中就需要手动切换10次语言,效率实在太低了
  经过研究通过参考一个开源项目,大约的摸索出了答案,直接公布给需要的人
  instruments这个工具提供了命令行的接口,我们可以通过命令行在程序启动的时候指定程序的语言
  给两个例子
  例子A - 在ios模拟器中运行一个app,指定语言为英语
instruments -t Desktop/sample1.tracetemplate -w "iPad - Simulator - iOS 7.1" "/Users/username/Library/Developer/Xcode/DerivedData/SOTC-LocalizationExample-dlqskvetzmqvcnbrpwdtldgtpubp/Build/Products/Debug-iphonesimulator/SOTC-LocalizationExample.app" -AppleLanguages "(en)" -AppleLocale en
  例子B - 在ipad真机中运行一个app,指定语言为德语
instruments -t Desktop/sample2.tracetemplate -w ac5c5801UDIDf2ca5e9e88a9db <app_name, e.g. QuickEdit> -AppleLanguages "(de)" -AppleLocale de
  其中我在末尾加了-AppleLanguages "(lan)" -AppleLocale lan来通知instruments把软件显示成不同的语言,很多人会疑问为什么有-AppleLanguages和-AppleLocale两个参数
  目前我测了几个软件,发现-AppleLanguages还是控制软件语言显示的主要值。换句话说,如果只有-AppleLocale没有-AppleLanguages,我测试的这几个软件都没有变语言。
  希望搜到这篇文章的同学能有所启发。

posted @ 2014-05-16 11:03 顺其自然EVO 阅读(412) | 评论 (0)编辑 收藏

UIAutomation: 通过命令行运行测试用例

 为了实现自动定时的运行脚本,我们需要在命令行运行和启动脚本,具体代码如下:
instruments -t /Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate
<full_path_to_application>
-e UIASCRIPT <path_to_script.js>
-e UIARESULTSPATH <output_results_path>
  1.  -t 后面的参数为 Automation.tracetemplate 的路径,每个版本的位置都有所不同,在命令行下使用 instruments -s 命令进行查询。
  2.  full_path_to_application 是你的的被测应用的完整目录,需要在前后加上双引号。
  3.  -e UIASCRIPT  指定执行的 js 脚本存放的位置,需要在前后加上双引号。
  4.  -e UIARESULTPATH  指定输出结果存放的路径,需要在前后加上双引号。
  5. 如果需要在真机运行,在-t 参数前加上  -w UDID, 其中UDID为被连接手机的设备标识(可通过iTools查看)。
  举例在我本地模拟器上的脚本如下:
instruments -t
/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate
"/Users/wangxinxin/Library/Developer/Xcode/DerivedData/TestAutomation-ahmqgmqipyidexemazbprqxqkrya/Build/Products/Debug-iphonesimulator/TestAutomation.app"
-e UIASCRIPT "/Users/wangxinxin/Documents/TestAutomation/test.js"
-e UIARESULTSPATH "/Users/wangxinxin/Documents/TestAutomation"
相关文章
UIAutomation: 登录界面测试实例

posted @ 2014-05-16 11:02 顺其自然EVO 阅读(253) | 评论 (0)编辑 收藏

手机APP测试几大点

移动互联网App测试点包括:
  1.安全测试
  1)软件权限
  -扣费风险:包括发送短信、拨打电话、连接网络等
  -隐私泄露风险:包括访问手机信息、访问联系人信息等
  -新增风险项
  2)开发者官方权限列表信息比对分析
  2.安装、运行、卸载测试
  验证App是否能正确安装、运行、卸载,以及操作过程和操作前后对系统资源的使用情况,主要包括:
  1)检测软件是否能正确安装、运行、卸载;
  2)安装、卸载、更新错误报告;
  3)其他辅助信息:
  -位置和文件夹是否合理;
  -组件是否正确注册或删除;
  -评估操作前后,CPU、Memory(内存占用)、Storage(磁盘占用)等系统资源的使用情况。
  3.UI测试
  测试用户界面(如菜单、对话框、窗口和其它可视控件)布局、风格是否满足客户要求,文字是否正确,页面是否美观,文字,图片组合是否完美,操作是否友好等。
  UI测试的目标是确保用户界面会通过测试对象的功能来为用户提供相应的访问或浏览功能。确保用户界面符合公司或行业的标准。包括用户友好性、人性化、易操作性测试。
  4.功能测试
  根据软件说明或用户需求验证App的各个功能实现,采用如下方法实现并评估功能测试过程:
  1)采用时间、地点、对象、行为和背景五元素或业务分析等方法分析、提炼App的用户使用场景,对比说明或需求,整理出内在、外在及非功能直接相关的需求,构建测试点,并明确测试标准(若用户需求中无明确标准遵循,则需要参考行业或相关国际标准或规则)。
  2)根据被测功能点的特性列举出相应类型的测试用例对其进行覆盖,如:涉及输入的地方需要考虑等价、边界、负面、异常或非法、场景回滚、关联测试等测试类型对其进行覆盖。
  3)在测试实现的各个阶段跟踪测试实现与需求输入的覆盖情况,及时修正业务或需求理解错误。
  5.性能测试
  评估App的时间和空间特性
  1)极限测试:在各种边界压力情况下(如电池、存储、网速等),验证App是否能正确响应。
  2)响应能力测试:测试App中的各类操作是否满足用户响应时间要求
  3)压力测试:反复/长期操作下,系统资源是否占用异常;
  4)性能评估:评估典型用户应用场景下,系统资源的使用情况。
  5)Benchmark测试(基线测试):与竞争产品的Benchmarking,产品演变对比测试等。
  6.中断测试
  针对智能终端应用的服务等级划分方式及实时特性所提出的测试方法,如:App在前/后台运行状态时与来电、文件下载、音乐收听等关键运用的交互情况测试等。
  7.兼容测试
  主要测试内部和外部兼容性,包括:
  与本地及主流App是否兼容; 检验在各种网络连接下(WiFi、GSM、GPRS、EDGE、WCDMA、CDMA1x、CDMA2000、HSPDA等),App的数据和运用是否正确;
  与各种设备是否兼容(若有跨系统支持则需要检验是否在各系统下,各种行为是否一致)。
  8.安全测试
  安全测试显得尤为重要,粗心、不谨慎的数据存储或传输方式使得非法、恶意目的有可乘之机。
  智能终端安全涉及各信息交互、存储接点,借鉴于网络传输和相关安全测试经验,App安全测试大概划分为以下几类:
  1)从数据的本地存储到数据的传输、处理以及远程访问等各个环节,基于相应的安全标准/行业标准评估App的安全特性;
  2)借鉴在Web App和网络安全测试的一些成功经验在智能终端App测试中进行裁减或适配;
  3)检测App的用户授权级别,数据泄漏,非法授权访问等;
  4)对App的输入有效性校验、认证、授权、敏感数据存储、数据加密等方面进行检测,以期发现潜在的安全问题;
  5)基于各种通信协议或相应的行业安全标准检视App是否满足相应的要求。
版权声明:本文出自 马兴草 的51Testing软件测试博客:http://www.51testing.com/?489243
原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。

posted @ 2014-05-16 11:02 顺其自然EVO 阅读(4523) | 评论 (0)编辑 收藏

谈disruptor的单线程数据库操作

对远程数据库的操作,采用disruptor能够很好解决死锁,
  首先是定义一个抽象类,实现Runnable接口
public abstract class  Task implements Runnable  {
public Task(){}
}
public class TaskEvent {
private Task tk;
public Task getTask() {
return tk;
}
public void setTask(Task tk) {
this.tk = tk;
}
public final static EventFactory<TaskEvent> EVENT_FACTORY = new EventFactory<TaskEvent>() {
public TaskEvent newInstance() {
return new TaskEvent();
}
};
public class TaskEventHandler implements EventHandler<TaskEvent> {
//  执行接口函数onEvent执行
public void onEvent(TaskEvent event, long sequence,
boolean endOfBatch) throws Exception {
event.getTask().run();
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.zhenhai.bonecp.CustomThreadFactory;
import com.zhenhai.disruptor.BatchEventProcessor;
import com.zhenhai.disruptor.RingBuffer;
import com.zhenhai.disruptor.SequenceBarrier;
import com.zhenhai.disruptor.YieldingWaitStrategy;
import com.zhenhai.disruptor.dsl.ProducerType;
/**
*     使用方法
DisruptorHelper.initAndStart();
Task tt=new Taska();
DisruptorHelper.produce(tt);
DisruptorHelper.shutdown();
*
*
*/
public class DisruptorHelper {
/**
* ringbuffer容量,最好是2的N次方
*/
private static final int BUFFER_SIZE = 1024 * 1;
private static int group=2;
private RingBuffer<TaskEvent> ringBuffer[];
private SequenceBarrier sequenceBarrier[];
private TaskEventHandler handler[];
private BatchEventProcessor<TaskEvent> batchEventProcessor[];
private  static DisruptorHelper instance;
private static boolean inited = false;
private static ScheduledExecutorService taskTimer=null;
//JDK 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
private    ExecutorService execute[];
//启动监视线程
static {
System.out.println("init DisruptorHelper!!!!!!!!!!!!!!!!!");
instance = new DisruptorHelper();
instance.init();
inited = true;
System.out.println("init DisruptorHelper end!!!!!!!!!!!!!!!!!");
}
**
* 静态类
* @return
*/
private DisruptorHelper(){ }
/**
* 初始化
*/
private void init(){
execute=new ExecutorService[group];
ringBuffer=new RingBuffer[group];
sequenceBarrier=new SequenceBarrier[group];
handler=new TaskEventHandler[group];
batchEventProcessor=new BatchEventProcessor[group];
////////////////定时执行////////////////
//初始化ringbuffer,存放Event
for(int i=0;i<group;i++){
ringBuffer[i] = RingBuffer.create(ProducerType.SINGLE, TaskEvent.EVENT_FACTORY, BUFFER_SIZE, new YieldingWaitStrategy());
sequenceBarrier[i] = ringBuffer[i].newBarrier();
handler[i] = new TaskEventHandler();
batchEventProcessor[i] = new BatchEventProcessor<TaskEvent>(ringBuffer[i], sequenceBarrier[i], handler[i]);
ringBuffer[i].addGatingSequences(batchEventProcessor[i].getSequence());
execute[i]= Executors.newSingleThreadExecutor();
execute[i].submit(instance.batchEventProcessor[i]);
}
this.taskTimer =  Executors.newScheduledThreadPool(10, new CustomThreadFactory("DisruptorHelper-scheduler", true));
inited = true;
}
/**
* 执行定时器
* @param tk
*/
private void produce(int index,Task tk){
//System.out.println("index:="+index);
if(index<0||index>=group) {
System.out.println("out of group index:="+index);
return;
}
// if capacity less than 10%, don't use ringbuffer anymore
System.out.println("capacity:="+ringBuffer[index].remainingCapacity());
if(ringBuffer[index].remainingCapacity() < BUFFER_SIZE * 0.1) {
System.out.println("disruptor:ringbuffer avaliable capacity is less than 10 %");
// do something
}else {
long sequence = ringBuffer[index].next();
//将状态报告存入ringBuffer的该序列号中
ringBuffer[index].get(sequence).setTask(tk);
//通知消费者该资源可以消费
ringBuffer[index].publish(sequence);
}
}
/**
* 获得容器的capacity的数量
* @param index
* @return
*/
private long  remainingcapacity(int index){
//System.out.println("index:="+index);
if(index<0||index>=group) {
System.out.println("out of group index:="+index);
return 0L;
}
long capacity= ringBuffer[index].remainingCapacity();
return capacity;
}
private void shutdown0(){
for(int i=0;i<group;i++){
execute[i].shutdown();
}
}
////////////////////////////////下面是静态方法提供调用////////////////////////////////////////////////////////
/**
* 直接消费
* @param tk
*/
public static void addTask(int priority,Task tk){
instance.produce(priority,tk);
}
/**
* 定时消费
* @param tk
* @param delay
* @param period
*/
public static void scheduleTask(int priority,Task tk,long delay,long period){
Runnable timerTask = new ScheduledTask(priority, tk);
taskTimer.scheduleAtFixedRate(timerTask, delay, period, TimeUnit.MILLISECONDS);
}
/**
* 定点执行
* @param tk
* @param hourse
* @param minus
* @param sec
* @return
*/
public static Runnable scheduleTask(int priority,Task tk, int hourse,int minus,int sec)
{
Runnable timerTask = new ScheduledTask(priority, tk);
//每天2:30分执行
long delay = Helper.calcDelay(hourse,minus,sec);
long period = Helper.ONE_DAY;
System.out.println("delay:"+(delay/1000)+"secs");
taskTimer.scheduleAtFixedRate(timerTask, delay, period, TimeUnit.MILLISECONDS);
return timerTask;
}
//对定时执行的程序进行分装
private static class ScheduledTask implements Runnable
{
private int priority;
private Task task;
ScheduledTask(int priority, Task task)
{
this.priority = priority;
this.task = task;
}
public void run()
{
try{
instance.produce(priority,task);
}catch(Exception e){
System.out.println("catch exception in DisruptorHelper!");
}
}
}
public static long getRemainingCapatiye(int index){
return instance.getRemainingCapatiye(index);
}
public static void shutdown(){
if(!inited){
throw new RuntimeException("Disruptor还没有初始化!");
}
instance.shutdown0();
}
}

posted @ 2014-05-15 11:53 顺其自然EVO 阅读(799) | 评论 (0)编辑 收藏

Linux关于sort命令的高级用法

 如果单纯地使用sort按行进行排序比较简单,
  但是使用sort按多个列值排列,同时使用tab作为分隔符,而且对于某些列需要进行逆序排列,这样sort命令写起来就比较麻烦了
  比如下面的文件内容,使用[TAB]进行分割:
Group-ID   Category-ID   Text        Frequency
----------------------------------------------
200        1000          oranges     10
200        900           bananas     5
200        1000          pears       8
200        1000          lemons      10
200        900           figs        4
190        700           grapes      17
  下面使用这些列进行排序(列4在列3之前进行排序,而且列4是逆序排列)
* Group ID (integer)
* Category ID (integer)
* Frequency “sorted in reverse order” (integer)
* Text (alpha-numeric)
  排序后的结果应该为:
Group-ID   Category-ID   Text        Frequency
----------------------------------------------
190        700           grapes      17
200        900           bananas     5
200        900           figs        4
200        1000          lemons      10
200        1000          oranges     10
200        1000          pears       8
  可以直接使用sort命令来解决这个问题:
  sort -t $'\t' -k 1n,1 -k 2n,2 -k4rn,4 -k3,3 <my-file>
  解释如下:
  -t $'\t':指定TAB为分隔符
  -k 1, 1: 按照第一列的值进行排序,如果只有一个1的话,相当于告诉sort从第一列开始直接到行尾排列
  n:代表是数字顺序,默认情况下市字典序,如10<2
  r: reverse 逆序排列,默认情况下市正序排列
  所以最后的命令:sort -t $'\t' -k 1n,1 -k 2n,2 -k4rn,4 -k3,3 my-file

posted @ 2014-05-15 11:53 顺其自然EVO 阅读(194) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 111 112 113 114 115 116 117 118 119 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜