一、UIAutomator

Android自动化测试工具有很多,但是要免费、易上手,本人觉得就直接使用Eclipse自带的UIAutomator就不错。测试人员无需跟开发要代码信息,只要手机上有安装之后的APP自己就能做出自动测试用例,况且一通百通,就算是不满足于UI测试的,找个简单易上手的先明白原理,再深入了解其它复杂工具也会轻松很多。何乐而不为呢?

UIAutomator是Eclipse自带的用于UI自动化测试工具,可仿真APP上的单击、滑动、输入文本等操作。

在使用之前,需要安装好java有关的JDK,SDK,然后配置java环境变量。关于安装JDK,SDK,配置JAVA环境变量网上有数不清的教程,这里就不废话了。直接进入主题。

还是简单介绍一下自动化测试代码中使用到的类关系:现在还不清楚也没有关系,先让程序跑起来,然后在使用API的过程中自然就理解了。

1、创建java工程

打开Eclipse,操作步骤: 点击“File—>New—>java Project”,然后打开如下图的界面:

其它的先不用管,既然要求必须输入Project name,那就输入。请用英文命名,很多时候中文会带来许多意想不到的问题。

输入Project name之后,点击“Next”进入下一步,看到的界面如下:

Source下的东西无需关注,点击“Libraries”,既然是调用java自带的UIAutomator,就要先引入UIAutomator使用到的包。导入uiautomator相关包:点击“Add External JARs”

先要找到自己的android.jar 和 Uiautomator.jar 包的位置,一般在安装文件下的adt-bundle-windows-x86-20130917\sdk\platforms\android-18\ 下,具体要找到自己电脑上的jar位置哦,

选中两个jar文件,“打开”导入。

此外还需要再加入本地库,点击“Add Library”,选择Junit,后点击“next”,使用默认的JUit3。点击“Finish”。

回到New Java Project,点击“Finish”即可创建工程成功。

2、创建class文件

刚刚创建的工程,Src下还没有任何东西,我们需要创建一个Class文件,用来写自动化程序代码。

创建class文件:项目名称右键——NEW——class,写class内容如下

2.1 import UIautomator两个包

2.2 class 名称必须和XXX.java名称相同

2.3每一个public开头,且以test命名开始的函数,系统就视为一个case,运行时方法的调用顺序并不是代码中的位置顺序,而是根据方法名的ASCII码大小顺序调用的。

2.4每一个class都必须继承自UiAutoMatorTestCase

3、生成xml 文件

找到target版本:>android list targets

Id:1

创建build.xml到测试工程中去,用于生成.jar文件:>Android create UItest-project –n testprojectname –t 1 –p testprojectpath

4.build jar 文件

4.1 Update Ant to 1.9

4.2 配置ant使用的javac版本:

右键build.xml—》RUN AS —》 External Tools Configurations。

        在main Tab 下输入Argumens: -Dbuild.compiler=javac1.7——》

Apply。

4.3配置sdk中的build.xml 文件E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\ant\build.xml: 找到javac,添加includeantruntime="false"

<javac encoding="${java.encoding}"

source="${java.source}" target="${java.target}"

debug="true" extdirs="" includeantruntime="false"

destdir="${out.classes.absolute.dir}"

bootclasspathref="project.target.class.path"

verbose="${verbose}"

classpathref="project.javac.classpath"

fork="${need.javac.fork}" includeantruntime="false">

<src path="${source.absolute.dir}" />

<src path="${gen.absolute.dir}" />

<compilerarg line="${java.compilerargs}" />

</javac>

5. push jar文件到手机

Cmd环境下需要先找到adb.exe所在位置目录,完整的命令语句:

adb push <path_to_output_jar>  /data/local/tmp

“<path_to_output_jar>”即要放到手机里的jar文件名及路径

相反的如果是从手机复制文件到电脑:

6. 运行测试

Cmd环境下需要先找到adb.exe所在位置目录,完整的命令语句:

adb shell uiautomator runtest  XXX.jar  -c  XXXClassname

备注:XXX.jar需要运行的jar文件名,

Classname: 需要运行jar中的哪个类的名字

7.测试结果

运行结果如下:

Current: 当前运行的测试编号,与方法名称相关

Class:当前运行的函数所在的类

Numtests:测试的总数,每一个public testXX就是一个测试数

Test: 当前测试的函数名称

INSTRUMENTAION_STATUS_CODE:测试状态码,1表示正在执行,0表示执行成功,-1编辑运行错误

二、使用UIAUTOMATORVIEWER获取APP控件

1.手机打开app

使用UIAutomator获取手机app控件时,先在手机上运行起对应app

2 .电脑连接手机

确保电脑与手机已连通。

先在cmd界面到达java的adb.exe所在位置,

运行命令“ >adb shell ”获取当前连接电脑的设备,如下图,如果adb shell 运行正常,会弹出“shell@hwH30-C00:/$”

有多个设备连接开发机器时(模拟器或真机),通过设置ANDROID_SERIAL环境变量指定需要截图的设备。比如下图的:shell@hwH30-C00:/$ set ANDROID_SERIAL= hwH30-C00,设置连接成功后,返回:“set ANDROID_SERIAL=hwH30-C00”

adb devices命令查看连接中的设备编号

如果当前只连接了一个设备,则不用再在Cmd中执行连接命令

3.运行uiautomatorviewer.bat

找到uiautomatorviewer.bat安装目录,本机中的位置如下:

E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\uiautomatorviewer.bat

双击运行uiautomatorviewer.bat,打开界面如下图:

上图中左半部分显示当前手机的呈现界面,若要获取其它界面的控件,需要在手机上后,点击上图左上角顶部的刷新,重新获取新界面。

上图右半部分显示当前界面的所有ui元素层即控件信息。右上半部分显示层级,右下部分显示指定层级上具体的控件属性,比如当前属性“class”:当前控件的所在class

“Package”:显示当前控制所在包

“Resource-id”:这个属性有最好了,有些Android开发人员没有为每个控件单独命名,在抓取的时候就非常不方便。如果有Resource-id,在抓取控件时,直接用Resource-id就能获取到正确的控件,并且操作正确,如果没有Resource-id,就只能通过查找同类控件后再按顺序获取了。比如先找到所有的textbox控件,再从所有的textbox控件中按序取几个,才能抓取到对应控件。

还有一些控件属性显示是否可见,是否checked,有实际应用需要时,可以获取

这里附上一个简单的测试java源代码,供参考代码格式

import com.android.uiautomator.core.UiDevice;

import com.android.uiautomator.core.UiObject;

import com.android.uiautomator.core.UiObjectNotFoundException;

import com.android.uiautomator.core.UiScrollable;

import com.android.uiautomator.core.UiSelector;

import com.android.uiautomator.testrunner.UiAutomatorTestCase;

public class MyTestCase extends UiAutomatorTestCase {

public void testDemo() throws UiObjectNotFoundException {

//点击home回到桌面

UiDevice device = getUiDevice();

device.pressHome();

// 点击并等待打开app

UiObject czmApp = new UiObject(new UiSelector().className(android.widget.TextView.class.getName()));

czmApp.clickAndWaitForNewWindow();

sleep(1000);

//登录;instan从0开始,

UiObject username= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/login_username_edit"));

username.clearTextField();

username.setText("15300000018");

UiObject pwd= new UiObject(new UiSelector().className("android.widget.EditText").enabled(true).resourceId("com.wlyc.warehousechampions:id/login_password_edit"));

pwd.clearTextField();

pwd.setText("00OA8C");

device.pressBack();

sleep(500);

UiObject enter= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/login_btn"));

enter.click();

sleep(5000);

//跳转到“我的”

UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));

My.clickAndWaitForNewWindow();

sleep(500);

//检查认证状态

UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));

int n=result.getText().length();

//认证审核中

n=Dur_verify(n);

//认证失败 或者认证成功

if(n==4)

{

UiObject failed= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/apply_certificate_layout"));

failed.click();

UiObject isedit= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_right_layout"));

//认证失败

if(isedit.exists())

{

isedit.click();

write();

sleep(500);

}

else //认证成功

{

UiObject backtoMy= new UiObject(new UiSelector().className("android.widget.LinearLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_left_layout"));

backtoMy.click();

Release();

}

}

n=result.getText().length();

n=Dur_verify(n);

Release();

}

//录入并提交企业认证

public void write()throws UiObjectNotFoundException

{

//跳转到企业认证

UiObject Company= new UiObject(new UiSelector().className("android.widget.RadioButton").resourceId("com.wlyc.warehousechampions:id/company_certificate"));

Company.click();

//录入企业认证信息

UiObject CompanyName=new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/cettificate_companyname_edit"));

CompanyName.setText("test");

sleep(500);

//拍照

UiObject photo= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.wlyc.warehousechampions:id/uploadimage_image"));

photo.click();

sleep(500);

UiObject takephoto= new UiObject(new UiSelector().className("android.widget.TextView").instance(1));

takephoto.click();

sleep(500);

UiObject phototake= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/shutter_button"));

phototake.click();

sleep(500);

UiObject photook= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/btn_done"));

photook.click();

sleep(500);

//提交认证

UiObject commit= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/commit_btn"));

commit.click();

sleep(500);

}

//发布仓库

public void Release() throws UiObjectNotFoundException

{

UiObject wh_release= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/warehouse_distribute"));

wh_release.click();

UiObject wh_name= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_name_edit"));

wh_name.setText("nametest");

UiObject wh_ctg= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout"));

wh_ctg.clickAndWaitForNewWindow();

UiObject wh_ctg1= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/spinnerview_listitem_text"));

wh_ctg1.click();

UiObject wh_mod= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout").instance(1));

wh_mod.click();

sleep(500);

UiObject wh_mod1= new UiObject(new UiSelector().className("android.widget.TextView").enabled(true).instance(0));

wh_mod1.click();

sleep(500);

UiObject wh_area= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_area_edit"));

wh_area.setText("5000");

UiObject wh_area_low= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_rentarea_edit"));

wh_area_low.setText("500");

UiObject wh_area_cpb= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_availrentarea_edit"));

wh_area_cpb.setText("1000");

UiObject wh_pric= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_price_edit"));

wh_pric.setText("26");

UiObject wh_etpris= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_company_edit"));

wh_etpris.setText("companyname");

UiObject wh_floor= new UiObject(new UiSelector().className("android.widget.CheckBox").enabled(true).instance(0));

wh_floor.click();

sleep(500);

//向下滑动屏幕

UiScrollable  wh_scroll=new UiScrollable(new UiSelector().className("com.wlyc.warehousechampions:id/pulltorefresh_listview"));

wh_scroll.setAsVerticalList();

;

UiObject wh_addre= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/warehousedetail_hightemperature_unit"));

wh_addre.click();

}

//认证审核中等待

public int Dur_verify(int n) throws UiObjectNotFoundException

{

while (n!=4) {

UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));

My.clickAndWaitForNewWindow();

sleep(5000);

UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));

n=result.getText().length();

}

return n;

}

}

三、Uiautomator Api分析

UiAutomator主要涉及以下几个类,大多数位于源码包的com.android.uiautomator.core下,其中粗体字部分为主要会接触到的类,熟知这5个类的作用,就可以大体顺畅的写出UiAutomator的测试用例。

UiAutomatorTestCase

UiDevice

UiSelector

UiScrollable

UiObject

UiCollection

UiTestAutomationBridge, UiAutomatorBridge

InteractionController, QueryController

UiWatcher

1、UiAutomatorTestCase

TestCase (Junit) -> UiAutomatorTestCase  -> App Test

每个测试用例(类)都需要继承UiAutomatorTestCase,以实现测试环境的setup,teardown(拆卸)等同能。而UiAutomatorTestCase则是通过继承Junit3中的TestCase类,并在其中的setUp() 、tearDown() 、getParams() 函数中。其中主要是用Bundle实现Android Activity之间的通讯。在UiAutomatorTestCase,还加入了getUiDevice()等关于UiDevice的 函数,以实现在测试的任意地方均可调用UiDevice()。

2、UiDevice

此类主要包含了获取设备状态信息,和模拟用户至于设备的操作两类api。

可以通过getDisplaySizeDp(), getDisplayWidth() , getDisplayHeight() ,getProductName() ,getCurrentActivityName(), getCurrentPackageName() 等获取设备相关信息。

pressMenu(), pressBack(), pressHome(), pressSearch() ,pressDPadCenter(), pressDPadRight(), pressDPadLeft(), pressDPadUp(), pressDPadDown() ,pressDelete(), pressEnter(), pressKeyCode(), pressRecentApps(),click(),swipe(),getDisplayRotation() setOrientationLeft()… wakeUp(), sleep() ,dumpWindowHierarchy(), waitForWindowUpdate()等API可以灵活的操纵设备。

而takeScreenshot() 允许随时对设备截屏。

3、UiSelector

主要是通过一定查询方式,定位到所要操作的UI元素。

一般UI元素均可通过以下API定位:text(), textMatches(String regex), textStartsWith(), textContains() ,className() ,classNameMatches(String regex), className(Class type) ,Description(), descriptionMatches(String regex),descriptionStartsWith(),descriptionContains() ,packageName(), packageNameMatches(String regex)。

值得注意的是index()和 instance() 两个函数,其中index()是当前页面的ID编号,instance()则表示在一定的搜索结果下,获取的子元素集的第几个元素。如:

new UiSelector().className("android.widget.ImageView").enabled(true).instance(2);

另有enabled(), focused(), focusable(), scrollable(), selected(), checked(), clickable() ,longClickable() ,childSelector()等检索条件,顾名思义。

4、UiObject

UiObject可代表页面的任意元素,它的各种属性定位通常通过UiSelector来完成。

比较常用的Api如clickAndWaitForNewWindow(),表示点击该元素,并且等待新窗口的展示完毕。这一过程是Android UI Testing框架支持的,不需要额外的控制等待时间。

UiObject允许点击该元素的具体一个部分,Api如clickTopLeft(), longClickBottomRight(),…

通过getText(), getContentDescription(), getVisibleBounds(),… 等api可获取UiObject的相关属性,getPackageName() 可用来明确是否打开了目标测试的App.

setText(), clearTextField() 可以 用来设置以及清空所关联的输入框。

waitForExists() 可以用来操纵相关等待或验证。

5、UiCollection

UiCollection一般与UiSelector连用,如它的构造函数也要求提供Uiselector: UiCollection(UiSelector selector)。

它的api较少,主要用以从Uiselector筛选出的元素集中挑出所要的元素:getChildByDescription(), getChildByInstance(), getChildByText() ,以及统计元素集的个数getChildCount()

6、UiScrollable

UiObject -> UiCollection ->UiScrollable

UiScrollable 用来表示可以滑动的界面元素,其继承关系如上图所示。

其Api中,setAsVerticalList(), setAsHorizontalList() 用以设置Ui元素列表是基于横向滚动还是纵向滚动。其后可以用getMaxSearchSwipes() ,flingForward(), flingBackward() ,scrollForward(),scrollBackward() ,scrollToEnd(), scrollToBeginning() 等函数控制滑动,以及getChildByDescription(), getChildByInstance(), getChildByText() ,scrollIntoView(), scrollTextIntoView(),… 来选择是否已经转换到具有目标元素的页面。如:

UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));

appViews.setAsHorizontalList();

UiObject helperApp;

helperApp = appViews.getChildByText(new UiSelector()

.className(android.widget.TextView.class.getName()), " 91助手 ");  则若当前页面没有91助手APP, 测试会自动滑动页面,直到91助手App出现。

下面介绍下UI Testing Framework构成的重要类:

7、UiTestAutomationBridge

这是整个Testing Framework的基础,此类负责连接系统了,记录最新的可链接事件(AccessibilityEvent) , 窗口内容查询Api等。可以被Android App调用,或者Java程序从shell调用。

这里需要注意两个概念:

7.1、AccessibilityEvent:所有的Ui元素可以被操纵,因为这些Event都是AccessibilityEvent。对于怎样令页面元素可以被操纵,使得相关的事件都是AccessibilityEvent,请参见Uiautomator 词条-"确认程序可以被测试" 部分。

7.2、AccessibilityNodeInfo:视窗中的组件树节点,也就是uiautomtorViewer中展示的各个节点。

Api中connect(), disconnect() 负责建立与设备的实际连接。

executeCommandAndWaitForAccessibilityEvent() performAccessibilityAction() findAccessibilityNodeInfosByText(), findAccessibilityNodeInfoByViewIdInActiveWindow() 都是其中重要的Api。

8、UiAutomatorBridge

UiAutomatorBridge是UiTestAutomationBridge的子类,区别主要是在构造函数中加上了InteractionController 和QueryController 两大对象的调用。以及一些常量定义等。除了上述差异,UiAutomatorBridge还定义了executeCommandAndWaitForAccessibilityEvent() 、onAccessibilityEvent() 、waitForIdle() 、addAccessibilityEventListener() 等函数。

9、InteractionController

介绍InteractionController,需要先提InteractionProvider,它负责注入用户事件(如点击、输入等) ,并且反应事件的对应坐标。

InteractionController则定义了几乎所有至于手机的基础操作,如runAndWaitForEvents(), clickAndWaitForEvents() ,click(), longTap(), scrollSwipe(),Swipe() ,clickAndWaitForNewWindow() ,touchUp(), touchDown(), TouchMove() ,isNaturalRotation(), setRotationRight(), setRotationLeft() ,freezeRotation() ,wakeDevice(), sleepDevice() 等。

10、QueryController

QueryController负责把UiSelector 的查找信息转化为AccessibilityNodeInfo。

具体Api如下:findNodePatternRecursive(), translatePatternSelector(), translateReqularSelector(), translateCompoundSelector(), getRootNode() ,findAccessibilityNodeInfo()。

11、UiWatcher

UiWatcher只在UiSelector无法找到匹配的结果时被调用,意在重试、等待页面更新 (如弹出对话框)等。其中只有一个主要函数:checkForCondition() 。

它的相关函数均在UiDevice中,如:UiDevice.registerWatcher() ,UiDevice. resetWatcherTriggers() ,UiDevice.runWatchers() ,UiDevice.removeWatcher()

android app自动化测试之UIAutomator的更多相关文章

  1. Android app自动化测试之Python+Appium环境搭建

    1.安装JDK (1)JDK安装时会有两次,一次是jdk,第二次是jre. (2)环境变量配置: 添加JAVA_HOME变量, 值:Jdk的安装路径 添加CLASSPATH变量,值: .;%JAVA_ ...

  2. Appium+Python app自动化测试之脚本启动和停止Appium服务

    研究了一段时间的Appium android app的自动化测试,工作中需要连接多台手机终端同时执行测试用例,我实现的方式是获取用例中需要执行用例的设备id个数以及实际连接到的设备数(通过adb de ...

  3. Android App性能测试之adb命令

    本篇文章总结了Android App性能测试过程中常用的adb命令.通过这些adb命令,可以查看App的性能数据,为评判性能好坏作参考. CPU相关 显示占用CPU最大的5个应用 adb shell ...

  4. android自动化测试之uiautomator

    1.通过monkeyrunner.bat   monkey_record.py启动MonkeyRecorder进行拿到各个控件的坐标(要连上手机才可以启动) 2.也可以通过uiautomatorvie ...

  5. app自动化测试之实战应用(百度app简单测试)

    模拟在百度app中搜索python相关内容代码如下: from appium import webdriver desired_caps = {} desired_caps['deviceName'] ...

  6. 【Python + ATX基于uiaotumator2】之Android—APP自动化简易例子

    上代码: import uiautomator2 as u2 from time import sleep d = u2.connect_usb('608ad0fe') #打开小卖 # d(text= ...

  7. app UI测试之UIAutomator

    执行UIAutomator测试步骤 1.新建Java项目,导入android.jar和uiautomator.jar包,继承UiAutomatorTestCase 2.生成编译文件 android c ...

  8. Android APP 性能测试之 GT 工具

    一.介绍: GT(随身调)是 APP 的随身调测平台,它是直接运行在手机上的"集成调测环境"(IDTE, Integrated Debug Environment).利用 GT,仅 ...

  9. Android App性能测试之二:CPU、流量

    CPU---监控值的获取方法.脚本实现和数据分析 1.获取CPU状态数据 adb shell dumpsys cpuinfo | findstr packagename 自动化测试脚本见cpustat ...

随机推荐

  1. 使用ruby过程中遇到安装gem失败的一些通用解决方案

    ruby语言升级还是比较勤快的.但是数量众多的版本使得程序库的兼容性成了大问题.有些gem表示明确不支持某个特定版本以前的ruby,而有些gem则与较高的版本不兼容.再加上gem本身也有版本,简直是乱 ...

  2. 杂谈X509证书, Java Keystore与Jetty

    很多人对JSSE组成部分的Key Tool 工具不太明白,希望本文能有帮助 科班出身的同学应该学过课程“密码学”, 这门课详细解释了现代对称加密的算法原理, 当时学的我云里雾里. 直到现在使用过SSL ...

  3. Atitit 桌面软件跨平台gui解决方案 javafx webview

    Atitit 桌面软件跨平台gui解决方案 javafx webview 1.1. 双向js交互1 1.2. 新弹出窗口解决1 1.3. 3.文档对象入口dom解析1 1.4. 所以果断JavaFX, ...

  4. 每天一个linux命令(21):find命令之xargs

    在使用 find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出 ...

  5. JavaScript到底是不是单线程

    JavaScript到底是不是单线程 JavaScript引擎 在了解计时器内部运作前,我们必须清楚一点,触发和执行并不是同一概念,计时器的回调函数一定会在指定delay的时间后被触发,但并不一定立即 ...

  6. 关于BUG率的计算和它的实际意义的思考

    我的微信号是Shalayang,以下是我的二维码名片,欢迎添加. 问题1:bug率有什么作用? my opion:用处有很多,需要具体情况具体分析,不过主要作用一般是来评价工作产品的质量.如果bug率 ...

  7. GOF设计模式特烦恼

    这段时间,学习状态比较一般,空闲时基本都在打游戏,和研究如何打好游戏,终于通过戏命师烬制霸LOL,玩笑了.为了和"学习"之间的友谊小船不翻,决定对以往学习过的GOF设计模式做一个简 ...

  8. JTMz换路径导致MySQL服务不能启动的问题

    问题: JTMz解压到我的机器上,运行一次后,把服务停止了,然后移到了另外一个路径,JTMz中自带的MySQL服务启动不起来了. 解决: 在注册表中修改 HKEY_LOCAL_MACHINE\SYST ...

  9. 自制jquery可编辑的下拉框

    昨天看到QQ登录的时候,可以选择以前登录过的账户,这个东西也可以在网站登录的时候用到,所以我就想做一个这样的插件:在网上查了很多,没有找到合适自己的,所以决定自动制作一个. 原理就是一个textbox ...

  10. javascript学习笔记(四):事件处理函数和动态创建html标记。

    1 HTML的事件属性  全局事件属性:HTML 4 增加了使事件在浏览器中触发动作的能力,比如当用户点击元素时启动 JavaScript. a. Window 事件属性,针对 window 对象触发 ...