PageSourceSearch

https://houlianpi.github.io/

html houlianpi.github.io collected 2026-10-03 09:18:18 UTC 61,423 bytes, 968 lines download raw bytes

1
2<!DOCTYPE html>
3<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
4<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
5<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
6<head>
7  <meta charset="utf-8">
8  <title>厚脸皮说测试</title>
9  <meta name="author" content="mijun(厚脸皮)">
10
11  
12  <meta name="description" content="Calabash Android 真的不错 Android自动化测试有着茫茫多的自动化测试框架,由于本人是Ruby的fans。所以对Ruby封装的一些工具情有独钟。虽然是Ruby的fans,但是对BDD一直不太能接受。最近正好有一个机会,开始尝试着使用Calabash &hellip;">
13  
14
15  <!-- http://t.co/dKP3o1e -->
16  <meta name="HandheldFriendly" content="True">
17  <meta name="MobileOptimized" content="320">
18  <meta name="viewport" content="width=device-width, initial-scale=1">
19
20  
21  <link rel="canonical" href="http://houlianpi.github.com/index.html">
22  <link href="/favicon.png" rel="icon">
23  <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
24  
24<script src="/javascripts/modernizr-2.0.js"></script>
24
25  
25<script src="/javascripts/ender.js"></script>
25
26  
26<script src="/javascripts/octopress.js" type="text/javascript"></script>
26
27  <link href="/atom.xml" rel="alternate" title="厚脸皮说测试" type="application/atom+xml">
28  <!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
29<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
30<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
31
32  
33  
33<script type="text/javascript">
34    var _gaq = _gaq || [];
35    _gaq.push(['_setAccount', 'UA-47053050-1']);
36    _gaq.push(['_trackPageview']);
37
38    (function() {
39      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
40      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
41      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
42    })();
43  </script>
43
44
45
46</head>
47
48<body   >
49  <header role="banner"><hgroup>
50  <h1><a href="/">厚脸皮说测试</a></h1>
51  
52    <h2>在这里我们只说测试</h2>
53  
54</hgroup>
55
56</header>
57  <nav role="navigation"><ul class="subscription" data-subscription="rss">
58  <li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
59  
60</ul>
61  
62<form action="http://google.com/search" method="get">
63  <fieldset role="search">
64    <input type="hidden" name="q" value="site:houlianpi.github.com" />
65    <input class="search" type="text" name="q" results="0" placeholder="Search"/>
66  </fieldset>
67</form>
68  
69<ul class="main-navigation">
70  <li><a href="/">Blog</a></li>
71  <li><a href="/blog/archives">Archives</a></li>
72  <li><a href="/about-me">About me</a></li>
73</ul>
74
75</nav>
76  <div id="main">
77    <div id="content">
78      <div class="blog-index">
79  
80  
81  
82    <article>
83      
84  <header>
85    
86      <h1 class="entry-title"><a href="/blog/calabash-setup-and-simple-use.html">Calabash Android 初始化和一些使用技巧</a></h1>
87    
88    
89      <p class="meta">
90        
91
92
93
94
95
96
97
98
99  
100
101
102<time datetime="2015-06-19T08:32:00+08:00" pubdate data-updated="true">Jun 19<span>th</span>, 2015</time>
103        
104      </p>
105    
106  </header>
107
108
109  <div class="entry-content"><h2>Calabash Android 真的不错</h2>
110
111<p>  Android自动化测试有着茫茫多的自动化测试框架,由于本人是Ruby的fans。所以对Ruby封装的一些工具情有独钟。虽然是Ruby的fans,但是对BDD一直不太能接受。最近正好有一个机会,开始尝试着使用Calabash Android自动化测试做一些东西。</p>
112
113<p>  首先,还是先废话几句吧。Android的自动化测试版本太多,不能在这里一一介绍。但是,现在国内使用的框架基本上可以分为两大类:Android JUnit系列和UiAutomator系列。</p>
114
115<p>  Android Junit系列就包括大名鼎鼎的<a href="https://code.google.com/p/robotium/">Robotium</a>和今天要说的<a href="https://github.com/calabash/calabash-android">Calabash Android</a>。这个系列除了有签名限制不能夸应用以外,都非常健壮和稳定。</p>
116
117<p>  UiAutomator系列最著名的当然要说是<a href="http://appium.io/">Appium</a>。UiAutomator可以跨应用操作,整体操作完全是基于UI的操作。但是Android官方对UiAutomator的支持并不是非常好,比如输入法等。使得UiAutomator的运行稳定性相对于Android JUnit差很多。再加上Android本身的版本分裂,UiAutomator那就更会让开发者觉得头大。所以,如果没有跨应用的操作,请尽量不要选择UiAutomator系列的工具,至少现在不要。</p>
118
119<p>  在Android JUnit这个系列上选择一款工具做自动化测试的话,个人更倾向于选择Calabash Android](https://github.com/calabash/calabash-android)或者<a href="http://selendroid.io/">Selendroid</a>。独立的QA团队更需要无源码方式的自动化测试框架。其次,简单高效稳定是任何自动化测试框架的基础。从这两点出发,选择了以上两款工具。<a href="http://selendroid.io/">Selendroid</a>是使用Java开发的,一般情况下需要使用Java来编写自动化测试用例。作为Ruby的fans更应该尝试一下Calabash Android](https://github.com/calabash/calabash-android)。</p>
120
121<h2>Calabash的环境配置</h2>
122
123<p>  Calabash的本身的安装相对简单,并且有<a href="https://github.com/calabash/calabash-android/blob/master/documentation/installation.md">官方文档</a>。现在安装的Calabash都是0.5版本的。</p>
124
125<p>  安装完成之后,进行简单的配置才可以使用Calabash。</p>
126
127<h4>1、配置环境变量</h4>
128
129<p>配置环境变量最关键点</p>
130
131<pre><code>export ANDROID_HOME=/Users/&lt;user&gt;/adt-bundle-mac-x86_64-20140702/sdk
132export PATH=$PATH:$ANDROID_HOME/tools
133</code></pre>
134
135<h4>2、生成签名,并且知道自己签名的password和alias</h4>
136
137<p>生成签名可以使用一下Shell来生成。</p>
138
139<pre><code>keytool -genkey -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"
140</code></pre>
141
142<p>生成之后签名的位置在</p>
143
144<pre><code>~/.android/debug.keystore
145</code></pre>
146
147<p>password是:</p>
148
149<pre><code>android
150</code></pre>
151
152<p>alias是:</p>
153
154<pre><code>androiddebugkey
155</code></pre>
156
157<p>以上这些关键元素在配置Calabash的时候都很重要。</p>
158
159<h4>3、配置Calabash</h4>
160
161<p>Calabash的配置一般情况加只需要配置签名相关信息就好。运行命令行:</p>
162
163<pre><code>calabash-android setup 520c4f587081a6b530a03c3e86c8bfc4.apk -v
164</code></pre>
165
166<p>然后按照提示分别输入,签名证书的位置,密码和alias。成功之后会看到这样一句提示:</p>
167
168<pre><code>Saved your settings to .calabash_settings. You can edit the settings manually or run this setup script again
169</code></pre>
170
171<p>当然,你也可以手动的配置文件完成设置。配置文件的地址为:</p>
172
173<pre><code>~/.calabash_settings
174</code></pre>
175
176<h4>4、Calabash Console</h4>
177
178<p>Calabash Console对于会写代码的人来说,一种非常方便调试的工具。使用命令:</p>
179
180<pre><code>calabash-android console 520c4f587081a6b530a03c3e86c8bfc4.apk
181</code></pre>
182
183<p>当然有时就会报错。报错有两种,一种是需要按照本文之前的内容进行Calabash配置。还有一种情况是需要对被测试应用进行重新签名。重新签名的命令:</p>
184
185<pre><code>calabash-android resign 520c4f587081a6b530a03c3e86c8bfc4.apk -v
186</code></pre>
187
188<p>成功进入Calabash Console之后,还需要运行两个命令行,才能开始传统意义上的自动化测试的调试:</p>
189
190<pre><code>reinstall_apps
191start_test_server_in_background
192</code></pre>
193
194<p>经过以上两条命令,被测试程序会启动,这是可以通过一些Calabash的命令进行自动化测试了。</p>
195
196<p>当然,也会有一些异常情况,例如,如果拿了网易新闻的Android客户端做这个实验,就会发现不会成功。因为网易新闻做了签名验证,当程序的签名被替换掉以后,程序无法正常启动。</p>
197</div>
198  
199  
200
201
202    </article>
203  
204  
205    <article>
206      
207  <header>
208    
209      <h1 class="entry-title"><a href="/blog/appium-chinese-input.html">Appium中文输入问题的一些探索</a></h1>
210    
211    
212      <p class="meta">
213        
214
215
216
217
218
219
220
221
222  
223
224
225<time datetime="2014-06-17T12:08:00+08:00" pubdate data-updated="true">Jun 17<span>th</span>, 2014</time>
226        
227      </p>
228    
229  </header>
230
231
232  <div class="entry-content"><h2>Appium输入中文的问题</h2>
233
234<p>在使用Appium做手机端的自动化测试时,你可以会遇到输入中文的问题。但是由于Appium是三个自动化测试工具的集合,所以遇到的中文问题也可能会比较难说清楚。Appium支持iOS、Android和FireFoxOS三种操作系统。但是FireFoxOS一般人都不用,所以,文章中它是最后一次露面了。</p>
235
236<p>Appium在iOS端自动化测试底层使用的是官方的<a href="https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html" title="Title">UI Autoamtion</a>。在Android端,4.2以上使用的官方的<a href="http://developer.android.com/tools/help/uiautomator/index.html" title="Title">Uiautomator</a>,4.1以下使用的时eBay的<a href="http://selendroid.io/" title="Title">selendroid</a>。所以在输入中文的问题上,三个平台理论上都有可能遇到问题。本文之后将重点调研Uiautomator的输入中文的问题。</p>
237
238<p>关于中文输入的的结论:</p>
239
240<ul>
241<li>Appium iOS  完全支持</li>
242<li>Appium Android selendroid 应该支持(需要实践确认,目前没有试过,只是猜测)</li>
243<li>Appium Android Uiautomator 不支持,应该是硬伤,目前无解</li>
244</ul>
245
246
247<p>selendroid的调研之后补充。</p>
248
249<h2>Appium iOS中文输入调研</h2>
250
251<p>首先先来说一下UI Automation。苹果官方的UI Automation在输入时有两种方法:</p>
252
253<ul>
254<li>(1)直接使用Element的setValue方法。</li>
255<li>(2)UI Automation中的 UIAKeyboard对象有一个typeString方法。</li>
256</ul>
257
258
259<p>以上两个方法在模拟器上都是完美支持中文的。所以不管在iOS上面怎么玩,都是支持中文的。当然两个方式是有区别的。方法(1)简单直接,基本上就是一个简单的set方法。输入时不会触发什么类似textChanged事件。方法(2)需要很多支持,方法二是完全模拟人手工输入过程。苹果对英文以外的输入都做了很好地兼容。</p>
260
261<p>Appium iOS版本的  sendkeys方法,直接对应  UIAKeyboard的typeString方法。由于typeString方法完美支持中文。所以Appium iOS版本也就支持中文了。当然,typeString方法也有缺点。当一个输入框内有内容的时候,typeString的输入方法是添加,所以,如果之前的内容不需要的时候,还需要先清除掉,在进行typeString。在没有特殊要求的情况下,我比较喜欢setValue这个方法,但是遗憾的时Appium不支持直接setValue。</p>
262
263<h2>Appium Android Uiautomator中文输入调研</h2>
264
265<p>之所以能有这篇文章,主要是因为最近想使用Appium做一些App之间相互跳转的自动化确认测试。一个中文的App,如果不能输入中文基本上就是歇菜。在使用学习工具的时候发现了很多坑,不能输入中文的这个坑最大,最郁闷。所以,把自己的调研方法写出来,供大家参考。</p>
266
267<p>首先 在一切未知的情况下,使用了这个方法进行输入:</p>
268
269<pre><code>input.sendKeys("舌尖上的中国");
270</code></pre>
271
272<p>发现结果不对,然后尝试了不输入中文:</p>
273
274<pre><code>input.sendKeys("ssssssss");
275</code></pre>
276
277<p>结果也不对,发现手机上使用的输入法是Google中文输入法。把输入法切换到英文输入法,以后,英文的正确的了,中文的还是有问题。于是Google了一下,找到了一个<a href="http://testerhome.com/topics/242" title="Title">帖子</a>。上面信息量很大,看起来比较靠谱的是有一个方法,利用JS运行器来直接输入方法,翻译为Java版本以后,代码如下:</p>
278
279<figure class='code'><figcaption><span>Java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
280<span class='line-number'>2</span>
281<span class='line-number'>3</span>
282</pre></td><td class='code'><pre><code class='java'><span class='line'>    <span class="n">inputDir</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&quot;element&quot;</span><span class="o">,((</span><span class="n">RemoteWebElement</span><span class="o">)</span><span class="n">input</span><span class="o">).</span><span class="na">getId</span><span class="o">());</span>
283</span><span class='line'>    <span class="n">inputDir</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&quot;text&quot;</span><span class="o">,</span><span class="s">&quot;舌尖上的中国&quot;</span><span class="o">);</span>
284</span><span class='line'>    <span class="n">driver</span><span class="o">.</span><span class="na">executeScript</span><span class="o">(</span><span class="s">&quot;element:setText&quot;</span><span class="o">,</span> <span class="n">inputDir</span><span class="o">);</span>
285</span></code></pre></td></tr></table></div></figure>
286
287
288<p>这个方法会出现一个没有实现的异常。</p>
289
290<p>好吧,看源码。</p>
291
292<p>在使用Appium 的sendkeys方法是,Client会发送一个http请求到Ap
292pium的server。在AppiumServer端的源码中 有一个 rounting 的文件,输入方法会发送这样的路由: rest.post(&#8216;/wd/hub/session/:sessionId?/element/:elementId?/value&#8217;, controller.setValue); 然后树藤摸瓜,找到controller的setValue方法。然后一步一步的查找,最终会找到bootstrap项目中的AndroidElement.java文件,其中有setText方法。代码如下:</p>
293
294<figure class='code'><figcaption><span>Java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
295<span class='line-number'>2</span>
296<span class='line-number'>3</span>
297<span class='line-number'>4</span>
298<span class='line-number'>5</span>
299<span class='line-number'>6</span>
300<span class='line-number'>7</span>
301<span class='line-number'>8</span>
302<span class='line-number'>9</span>
303<span class='line-number'>10</span>
304</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">setText</span><span class="o">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">text</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">UiObjectNotFoundException</span> <span class="o">{</span>
305</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">UnicodeEncoder</span><span class="o">.</span><span class="na">needsEncoding</span><span class="o">(</span><span class="n">text</span><span class="o">))</span> <span class="o">{</span>
306</span><span class='line'>      <span class="n">Logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">&quot;Sending Unicode text to element: &quot;</span> <span class="o">+</span> <span class="n">text</span><span class="o">);</span>
307</span><span class='line'>      <span class="n">String</span> <span class="n">encodedText</span> <span class="o">=</span> <span class="n">UnicodeEncoder</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">text</span><span class="o">);</span>
308</span><span class='line'>      <span class="k">return</span> <span class="n">el</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">encodedText</span><span class="o">);</span>
309</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
310</span><span class='line'>      <span class="n">Logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">&quot;Sending plain text to element: &quot;</span> <span class="o">+</span> <span class="n">text</span><span class="o">);</span>
311</span><span class='line'>      <span class="k">return</span> <span class="n">el</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">text</span><span class="o">);</span>
312</span><span class='line'>    <span class="o">}</span>
313</span><span class='line'>  <span class="o">}</span>
314</span></code></pre></td></tr></table></div></figure>
315
316
317<p>在Appium Android Uiautomator中,输入方法就是这一段代码。从这一段代码中会发现Appium基本没有做什么事情,最终调用了Uiautomator本身的setText方法。所以,接下来需要确认原生的Uiautomator是否可以支持中文输入。
318经过简单的测试发现,Uiautomator不支持中文输入,然后继续Google解决方案。发现了一个<a href="https://github.com/sumio/uiautomator-unicode-input-helper" title="Title">uiautomator-unicode-input-helper</a>开源项目。直接拿来尝试了中文,也发现不对。按照作者的说法是可以支持日语,但是貌似想支持中文的话,还要进行一定量的开发。</p>
319
320<p>继续看看Uiautomator为什么不支持中文。然后继续找Android的源码,发现Uiautomator的所有的功能都是通过UiAutomatorBridge来完成的。在UiAutomatorBridge类中有一个成员变量类型是InteractionController。输入是通过InteractionController来实现的。具体的setText方法ä
320»£ç å¦‚下:</p>
321
322<figure class='code'><figcaption><span>Java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
323<span class='line-number'>2</span>
324<span class='line-number'>3</span>
325<span class='line-number'>4</span>
326<span class='line-number'>5</span>
327<span class='line-number'>6</span>
328<span class='line-number'>7</span>
329<span class='line-number'>8</span>
330<span class='line-number'>9</span>
331<span class='line-number'>10</span>
332<span class='line-number'>11</span>
333<span class='line-number'>12</span>
334<span class='line-number'>13</span>
335<span class='line-number'>14</span>
336<span class='line-number'>15</span>
337<span class='line-number'>16</span>
338<span class='line-number'>17</span>
339<span class='line-number'>18</span>
340<span class='line-number'>19</span>
341<span class='line-number'>20</span>
342<span class='line-number'>21</span>
343<span class='line-number'>22</span>
344</pre></td><td class='code'><pre><code class='java'><span class='line'>   <span class="kd">public</span> <span class="kt">boolean</span> <span class="n">More</span> <span class="o">...</span><span class="na">sendText</span><span class="o">(</span><span class="n">String</span> <span class="n">text</span><span class="o">)</span> <span class="o">{</span>
345</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">DEBUG</span><span class="o">)</span> <span class="o">{</span>
346</span><span class='line'>                    <span class="n">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="n">LOG_TAG</span><span class="o">,</span> <span class="s">&quot;sendText (&quot;</span> <span class="o">+</span> <span class="n">text</span> <span class="o">+</span> <span class="s">&quot;)&quot;</span><span class="o">);</span>
347</span><span class='line'>        <span class="o">}</span>
348</span><span class='line'>        <span class="n">mUiAutomatorBridge</span><span class="o">.</span><span class="na">setOperationTime</span><span class="o">();</span>
349</span><span class='line'>        <span class="n">KeyEvent</span><span class="o">[]</span> <span class="n">events</span> <span class="o">=</span> <span class="n">mKeyCharacterMap</span><span class="o">.</span><span class="na">getEvents</span><span class="o">(</span><span class="n">text</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">());</span>
350</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">events</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
351</span><span class='line'>            <span class="k">for</span> <span class="o">(</span><span class="n">KeyEvent</span> <span class="n">event2</span> <span class="o">:</span> <span class="n">events</span><span class="o">)</span> <span class="o">{</span>
352</span><span class='line'>                <span class="c1">// We have to change the time of an event before injecting it because</span>
353</span><span class='line'>                <span class="c1">// all KeyEvents returned by KeyCharacterMap.getEvents() have the same</span>
354</span><span class='line'>                <span class="c1">// time stamp and the system rejects too old events. Hence, it is</span>
355</span><span class='line'>                <span class="c1">// possible for an event to become stale before it is injected if it</span>
356</span><span class='line'>                <span class="c1">// takes too long to inject the preceding ones.</span>
357</span><span class='line'>                <span class="n">KeyEvent</span> <span class="n">event</span> <span class="o">=</span> <span class="n">KeyEvent</span><span class="o">.</span><span class="na">changeTimeRepeat</span><span class="o">(</span><span class="n">event2</span><span class="o">,</span>
358</span><span class='line'>                        <span class="n">SystemClock</span><span class="o">.</span><span class="na">uptimeMillis</span><span class="o">(),</span> <span class="mi">0</span><span class="o">);</span>
359</span><span class='line'>
359                <span class="k">if</span> <span class="o">(!</span><span class="n">injectEventSync</span><span class="o">(</span><span class="n">event</span><span class="o">))</span> <span class="o">{</span>
360</span><span class='line'>                    <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
361</span><span class='line'>                <span class="o">}</span>
362</span><span class='line'>            <span class="o">}</span>
363</span><span class='line'>        <span class="o">}</span>
364</span><span class='line'>        <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
365</span><span class='line'>    <span class="o">}</span>
366</span></code></pre></td></tr></table></div></figure>
367
368
369<p>通过这段代码,基本上就明白了,Android的Uiautomator完全没有兼容英文以外的输入法的意思。并且也没有提供直接设置属性的方法。</p>
370
371<p>Appium Android Uiautomator 要支持输入中文还有很长的一段路需要走。伤心了。</p>
372</div>
373  
374  
375
376
377    </article>
378  
379  
380    <article>
381      
382  <header>
383    
384      <h1 class="entry-title"><a href="/blog/kan-wu.html">《iOS测试指南》勘误一</a></h1>
385    
386    
387      <p class="meta">
388        
389
390
391
392
393
394
395
396
397  
398
399
400<time datetime="2014-06-01T21:00:00+08:00" pubdate data-updated="true">Jun 1<span>st</span>, 2014</time>
401        
402      </p>
403    
404  </header>
405
406
407  <div class="entry-content"><p>《iOS测试指南》出版并且上市已经有一个月的时间了。随着该书陆续的与读者见面,一些隐藏着的小错误也慢慢浮出水面。在这里先简单的说明一下,截止到现在笔者所知的一些书内的错误。</p>
408
409<h3>Byte 和 bit的区别</h3>
410
411<p>Byte和bit是有区别,8个bit是一个Byte。一般说文件大小的时候,单位是Byte。说网络带宽时一般使用bit。在《iOS测试指南》的125页中,在描述几种手机上网带宽时,写错了,网络带宽的地址。</p>
412
413<p>书中原始如图所示,这里应该使用 kb,来表示网络带宽,并不是使用KB。
414<img src="http://ww3.sinaimg.cn/mw690/9d98efcctw1egywk2uikxj21cq0hagpm.jpg" title="125页中错误截图" alt="alt text" /></p>
415
416<p>非常抱歉,一个简单的疏忽就让网络带宽原地提高了8倍,真可谓差之毫厘,失之千里。</p>
417
418<h3>千万不要去Copy代码</h3>
419
420<p>我在写《iOS测试指南》的过程中,书稿经过了好几次修改,但是依然还是能发现一些错别字。文字功底已经没有办法挽回了,只能认真的把实例代码写好了。所以,我都很认真的写着示例代码。保证所有的代码都是运行过,并且没有问题的才写进到书中。“代码运行过,不代表没有问题。”这是测试工程师经常拿来教育开发工程师的话,自己也犯错误了。</p>
421
422<p>在书中的第43页,有一段代码中的注释有问题。</p>
423
424<p><img src="http://ww1.sinaimg.cn/mw690/9d98efcctw1egywk3rum6j21k00umn85.jpg" title="43页中错误截图" alt="alt text" /></p>
425
426<p>注释写错了,很明显这不是一个笔误。这是一个复制之后没有修改完全的错误。在编写代码的过程中千万不要去Copy代码。没有Copy就没有低级错误。</p>
427
428<h3>iOS 7的改动带来的问题</h3>
429
430<p>在本书中使用了很多次Recipse程序,Recipse程序是官方文档中用来介绍UI Automation的示例程序。本书的第八章介绍KIF时,也是使用了Recipse程序来完成一些功能测试。当需要删除Recipse程序中的菜单时,系统提供的TableViewCell会有删除确认。但是那个确认的按钮的一些属性有些变化。如图所示。</p>
431
432<p><img src="http://ww3.sinaimg.cn/mw690/9d98efcctw1egyy44qotaj21z41721kx.jpg" title="删除确认截图" alt="alt text" /></p>
433
434<p>图中红框内的属性和iOS 6中的属性已经有所变化。</p>
435
436<p>在 iOS 7.0以上的版本,删除确认按钮的属性是 Delete。但是在iOS 6或者5中,删除确认按钮的属性是 Confirm Deletion for xxx(xxx 是 TableCell的name属性)</p>
437
438<p>所以为了测试能在iOS所有的系统上都能完美的运行,代码可以这样修改:</p>
439
440<figure class='code'><figcaption><span>Objective-C</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
441<span class='line-number'>2</span>
442<span class='line-number'>3</span>
443<span class='line-number'>4</span>
444<span class='line-number'>5</span>
445<span class='line-number'>6</span>
446<span class='line-number'>7</span>
447<span class='line-number'>8</span>
448</pre></td><td class='code'><pre><code class='objectivec'><span class='line'><span class="c1">//点击删除确认按钮</span>
449</span><span class='line'><span class="k">if</span> <span class="p">([[[</span><span class="n">UIDevice</span> <span class="n">currentDevice</span><span class="p">]</span> <span class="n">systemVersion</span><span class="p">]</span> <span class="n">floatValue</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mf">7.0</span><span class="p">)</span> <span class="p">{</span>
450</span><span class='line'>    <span class="p">[</span><span class="n">tester</span> <span class="nl">tapViewWithAccessibilityLabel:</span><span class="s">@&quot;Confirm Deletion for aaa&quot;</span>
451</span><span class='line'>
451                                   <span class="nl">traits:</span><span class="n">UIAccessibilityTraitButton</span><span class="p">];</span>
452</span><span class='line'><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
453</span><span class='line'>    <span class="p">[</span><span class="n">tester</span> <span class="nl">tapViewWithAccessibilityLabel:</span><span class="s">@&quot;Delete&quot;</span>
454</span><span class='line'>                                   <span class="nl">traits:</span><span class="n">UIAccessibilityTraitButton</span><span class="p">];</span>
455</span><span class='line'><span class="p">}</span>
456</span></code></pre></td></tr></table></div></figure>
457
458
459<p>当然,因为属性是很多自动化测试工具都要用到的,所以,大家在iOS6升级到iOS7时都会有这个问题。当你使用的UI Automation的时候,也会遇到这个问题。当然,在UI Automation的时候,还有更大的一个问题。在iOS 7系统中,由于Apple公司的问题,导致UI Automation的所有滑动操作无效。</p>
460
461<p>由于iOS7中滑动操作无效,所以,只能使用别的操作路径来进行删除。</p>
462
463<p>完整的代码如下:</p>
464
465<figure class='code'><figcaption><span>JavaScript</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
466<span class='line-number'>2</span>
467<span class='line-number'>3</span>
468<span class='line-number'>4</span>
469<span class='line-number'>5</span>
470<span class='line-number'>6</span>
471<span class='line-number'>7</span>
472<span class='line-number'>8</span>
473<span class='line-number'>9</span>
474</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">test</span><span class="p">(</span><span class="s2">&quot;删除刚才添加的饺子菜单,通过判断总菜单数量的变化来判断测试是否成功&quot;</span><span class="p">,</span>
475</span><span class='line'>    <span class="kd">function</span><span class="p">(){</span>
476</span><span class='line'>        <span class="nx">oldCellsLength</span> <span class="o">=</span> <span class="nx">Finder</span><span class="p">.</span><span class="nx">findElementByName</span><span class="p">(</span><span class="s2">&quot;Empty list&quot;</span><span class="p">).</span><span class="nx">cells</span><span class="p">().</span><span class="nx">length</span>
477</span><span class='line'>        <span class="nx">Finder</span><span class="p">.</span><span class="nx">findElementByName</span><span class="p">(</span><span class="s2">&quot;Edit&quot;</span><span class="p">).</span><span class="nx">tap</span><span class="p">();</span>
478</span><span class='line'>        <span class="nx">Finder</span><span class="p">.</span><span class="nx">findElementByName</span><span class="p">(</span><span class="s2">&quot;Delete 饺子&quot;</span><span class="p">).</span><span class="nx">tap</span><span class="p">();</span>
479</span><span class='line'>        <span class="nx">Finder</span><span class="p">.</span><span class="nx">findElementByName</span><span class="p">(</span><span class="s2">&quot;饺子&quot;</span><span class="p">).</span><span class="nx">buttons</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">tap</span><span class="p">();</span>
480</span><span class='line'>        <span class="nx">newCellsLength</span> <span class="o">=</span> <span class="nx">Finder</span><span class="p">.</span><span class="nx">findElementsByClassType</span><span class="p">(</span><span class="s2">&quot;TableCell&quot;</span><span class="p">).</span><span class="nx">length</span>
481</span><span class='line'>        <span class="nx">assertEquals</span><span class="p">(</span><span class="nx">newCellsLength</span><span class="p">,</span><span class="nx">oldCellsLength</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
482</span><span class='line'>    <span class="p">}</span>
483</span></code></pre></td></tr></table></div></figure>
484
485
486<p>以上两处需要进行代码更改的地方,都是Apple升级iOS7以后,需要针对iOS7进行的一些容错处理。需要容错的主要由2处:</p>
487
488<ol>
489<li><p>iOS 7 中 TableCells中自带的删除按钮的Accessbility属性发生了变化。有原来的 “Confirm Deletion for xxx(xxx 是 TableCell的name属性)” 变化为  “Delete”。</p></li>
490<li><p>UI Autoamtion的所有滑动相关的手势无效。</p></li>
491</ol>
492
493
494<p>针对以上的内容需要进行iOS7相关的适配。适配方法请仔细阅读代码。《iOS测试指南》出版的时候,iOS7已经发了,但是未能对iOS7中的内容进行全面的兼容,在此表示深深的歉意。</p>
495</div>
496  
497  
498
499
500    </article>
501  
502  
503    <article>
504      
505  <header>
506    
507      <h1 class="entry-title"><a href="/blog/ios-testframework.html">这些年用过的iOS测试框架</a></h1>
508    
509    
510      <p class="meta">
511        
512
513
514
515
516
517
518
519
520  
521
522
523<time datetime="2014-01-16T00:37:00+08:00" pubdate data-updated="true">Jan 16<span>th</span>, 2014</time>
524        
525      </p>
526    
527  </header>
528
529
530  <div class="entry-content"><p>   iOS自动化测试一直很神秘,很多人都在探索和找寻最强大的那一款自动化测试工具。个人观点,没有最强大只有最适合,适合以后使用熟练了自然功能强大了。</p>
531
532<p>   写在正文之前的几句废话。说到功能自动化测试一般特指基于UI层面的自动化,本文中介绍的自动化测试框架都是UI自动化测试框架。更大概念的自动化测试以后有时间再详细介绍吧。说到UI自动化测试,就会有不同的一些声音。说什么UI自动化测试投入产出比低,UI变化快脚本维护成本高等。其实,这是一个怎么看待UI自动化测试问题。个人认为适量的UI自动化测试投入产出比还是很高的,具体“适量”怎么理解需要看具体的场景。如果你觉得UI自动化一无是处,那么看到这里就可以千万别往下看了。</p>
533
534<p>   iOS到底有多少测试框架?大家一直在总结,之前stackoverflow上有一篇神帖总结过不下20种,当然不只是UI自动化测试框架,还包括单元测试框架什么的。(链接找不到了,由于时间比较老了很多工具已经不维护或者很少人使用,不看也罢)</p>
535
536<p>   最近发现了一篇Blog <a href="http://watirmelon.com/2013/11/04/the-current-state-of-ios-automated-functional-testing/" title="Title">The current state of iOS automated functional testing</a>总结了一些UI自动化框架,总结的比较到位推荐给大家。结合自己的使用经验,对该Blog进行一些补充,希望您喜欢。</p>
537
538<p>按照iOS自动化测试框架的实现原理来划分,iOS自动化测试框架大致可以分为两个大类4种类型:</p>
539
540<ul>
541<li>UI Automationç³»
542
543<ul>
544<li>扩展型UI Automation</li>
545<li>驱动型UI Automation</li>
546</ul>
547</li>
548<li>非 UI Automation系
549
550<ul>
551<li>私有API型</li>
552<li>注入编译型</li>
553</ul>
554</li>
555</ul>
556
557
558<h2>UI Automation</h2>
559
560<p>UI Automation是Apple官方提供的UI自动化测试的解决方法,虽然直接使用非常不爽,但是还不得不说。虽然不好用,但是作为一些工具的底层实现还是需要了解的。<a href="https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html" title="Title">官方教程</a> 和 <a href="https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html" title="Title">相关API文档</a> 可以方便查阅</p>
561
562<h3>扩展型UI Automation</h3>
563
564<p><a href="https://github.com/alexvollmer/tuneup_js" title="title">TuneupJs</a>是最早的iOS自动化测试工具,以JavaScript扩展库方法提供了很多好用js工具,最重要的是提供了超简洁的单元测试框架和持续继承解决方案。</p>
565
566<p><a href="https://github.com/douban/ynm3k" title="title">ynm3k</a>是笔者维护的iOS自动化测试框架,借鉴或者说抄袭了很多TuneUpJs的想法,在其基础上加入了UI控件定位的很多方法,让测试脚本更加简单便捷。当然之后还有后续的维护计划,过了变态的996以后会开始实施。</p>
567
568<p>如果你偏爱这种类型的测试框架,我真心的推荐ynm3k。因为TuneUpJS已经不维护了,ynm3k还会做一些好玩的功能。最关键的是我觉得ynm3k的UI控件定位识别功能真的很棒。(老王卖瓜了这么长时间还请大家见谅)</p>
569
570<h3>驱动型UI Automation</h3>
571
572<p>驱动型UI Automation 在自动化测试底层使用了UI Automation库,通过TCP通信的方式驱动UI Automation来完成自动化测试。通过这种方式,编辑脚本的语言不在局限于JavaScript,理论上讲可以是任何一种语言。所以有了<a href="http://ios-driver.github.io/ios-driver/index.html">iOSDriver</a>和<a href="http://appium.io/">Appium</a>.</p>
573
574<p>iOSDriver和Appium还兼容了WebDriver的Json Wire Protocol协议,意味着你可以写WebDriver脚本来完成iOS自动化测试。当然在手机端的一些操作和Web端是不同的,iOSDriver和Appium都扩展了相关的功能。iOSDriver选择了多加入Java语言Jar包的方式支持,而Appium则选择了通过WebDriver注入JavaScript的方式支持。iOSDriver的实现方式决定了只能使用Java语言编辑脚本,而Appium则支持更多的语言。</p>
575
576<p>当然让我真正选择Appium的原因是因为Appium更加轻便易用一些。如果你不经常玩Java,使用iOSDriver的时候一定会在环境设置方面卡很é•
576¿çš„æ—¶é—´ã€‚</p>
577
578<h3>私有API型</h3>
579
580<p>直接使用私有API对UI界面进行操作是最简洁有效的自动化测试方式。私有API结合iOS单元测试框架OCUnit的组合完全非常棒,至少对iOS开发工程师来说。在这种类型的测试框架中,<a href="https://github.com/kif-framework/KIF">KIF</a>是必须介绍的。原因很简单,Google在使用。对于那些对Google无理由崇拜的人们,赶紧用起来吧。</p>
581
582<p>笔者关注了KIF一段时间,起初1.0版本的时候,真心的不好用。当然工具的维护者也意识到了这个方面的问题,推出了KIF的2.0版本,更新了很多的API。在2.0版本时,还不错。如果想做纯UI界面操作的话,不推荐使用KIF。</p>
583
584<h3>注入编译型</h3>
585
586<p>注入编译型是指在编译时注入一个Server到App内部,通过Server对外通信完成UI操作指令的执行。其中最著名的代表为<a href="http://www.testingwithfrank.com/">Frank</a>和<a href="http://calaba.sh/">Calabash</a>. 它们还是BDD测试框架的杰出代表。维护大单位都是全球知名的敏捷咨询公司。喜欢cucumber和ruby的人可以考虑。</p>
587
588<p>大概介绍到这里,之后会陆续进行每种类型的详细介绍。</p>
589</div>
590  
591  
592
593
594    </article>
595  
596  
597    <article>
598      
599  <header>
600    
601      <h1 class="entry-title"><a href="/blog/applescript-hong-fa-mo-ni-qi-nei-cun-gao-jing.html">UI Automation 中触发iOS模拟器内存警告</a></h1>
602    
603    
604      <p class="meta">
605        
606
607
608
609
610
611
612
613
614  
615
616
617<time datetime="2014-01-09T19:18:00+08:00" pubdate data-updated="true">Jan 9<span>th</span>, 2014</time>
618        
619      </p>
620    
621  </header>
622
623
624  <div class="entry-content"><p>在UI Automation的api中没有提供模拟内存警告的接口。在Monkey测试的过程中 MemoryWarning还是非常有用的。所以需要想办法自己封一个simulateMemoryWarning接口。</p>
625
626<p>经过调研,决定采用AppleScript来实现功能,然后在UI Automation中使用host.performTaskWithPathArgumentsTimeout调用AppleScript方式来实现。</p>
627
628<p>调试AppleScript。在<a href="http://stackoverflow.com/questions/2784892/simulate-memory-warnings-from-the-code-possible" title="Title">stackoverflow</a> 中,可以找到OC和AppleScript版本的内存告警实现。其中AppleScript的实现为:</p>
629
630<figure class='code'><figcaption><span>AppleScript simulate memory warning</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
631<span class='line-number'>2</span>
632<span class='line-number'>3</span>
633<span class='line-number'>4</span>
634<span class='line-number'>5</span>
635<span class='line-number'>6</span>
636<span class='line-number'>7</span>
637<span class='line-number'>8</span>
638<span class='line-number'>9</span>
639<span class='line-number'>10</span>
640<span class='line-number'>11</span>
641</pre></td><td class='code'><pre><code class='applescript'><span class='line'><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;System Events&quot;</span>
642</span><span class='line'>  <span class="k">tell</span> <span class="nv">process</span> <span class="s2">&quot;iPhone Simulator&quot;</span>
643</span><span class='line'>      <span class="k">tell</span> <span class="na">menu</span> <span class="nv">bar</span> <span class="mi">1</span>
644</span><span class='line'>          <span class="k">tell</span> <span class="na">menu</span> <span class="nv">bar</span> <span class="nb">item</span> <span class="s2">&quot;Hardware&quot;</span>
645</span><span class='line'>              <span class="k">tell</span> <span class="na">menu</span> <span class="s2">&quot;Hardware&quot;</span>
646</span><span class='line'>                  <span class="nv">click</span> <span class="na">menu</span> <span class="nb">item</span> <span class="s2">&quot;Simulate Memory Warning&quot;</span>
647</span><span class='line'>              <span class="k">end</span> <span class="k">tell</span>
648</span><span class='line'>          <span class="k">end</span> <span class="k">tell</span>
649</span><span class='line'>
649      <span class="k">end</span> <span class="k">tell</span>
650</span><span class='line'>  <span class="k">end</span> <span class="k">tell</span>
651</span><span class='line'><span class="k">end</span> <span class="k">tell</span>
652</span></code></pre></td></tr></table></div></figure>
653
654
655<p>简单的调试发现不稳定,最好加入一下代码:</p>
656
657<figure class='code'><figcaption><span>AppleScript make sure iphone simulator activate</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
658<span class='line-number'>2</span>
659<span class='line-number'>3</span>
660</pre></td><td class='code'><pre><code class='applescript'><span class='line'><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;iPhone Simulator&quot;</span>
661</span><span class='line'>  <span class="nb">activate</span>
662</span><span class='line'><span class="k">end</span> <span class="k">tell</span>
663</span></code></pre></td></tr></table></div></figure>
664
665
666<p>之前的脚本只能支持英文版操作系统,为了兼容中文操作系统,再次加入代码:</p>
667
668<figure class='code'><figcaption><span>AppleScript get os language & simulate memory warning</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
669<span class='line-number'>2</span>
670<span class='line-number'>3</span>
671<span class='line-number'>4</span>
672<span class='line-number'>5</span>
673<span class='line-number'>6</span>
674<span class='line-number'>7</span>
675<span class='line-number'>8</span>
676<span class='line-number'>9</span>
677<span class='line-number'>10</span>
678<span class='line-number'>11</span>
679<span class='line-number'>12</span>
680<span class='line-number'>13</span>
681<span class='line-number'>14</span>
682<span class='line-number'>15</span>
683<span class='line-number'>16</span>
684<span class='line-number'>17</span>
685<span class='line-number'>18</span>
686<span class='line-number'>19</span>
687<span class='line-number'>20</span>
688<span class='line-number'>21</span>
689<span class='line-number'>22</span>
690<span class='line-number'>23</span>
691</pre></td><td class='code'><pre><code class='applescript'><span class='line'><span class="k">on</span> <span class="nv">get_language</span><span class="p">()</span>
692</span><span class='line'>  <span class="no">return</span> <span class="nv">user</span> <span class="nv">locale</span> <span class="k">of</span> <span class="p">(</span><span class="k">get</span> <span class="nb">system info</span><span class="p">)</span>
693</span><span class='line'><span class="k">end</span> <span class="nv">get_language</span>
694</span><span class='line'><span class="k">set</span> <span class="nv">menubar</span> <span class="k">to</span> <span class="s2">&quot;Hardware&quot;</span>
695</span><span class='line'><span class="k">set</span> <span class="nv">menuclick</span> <span class="k">to</span> <span class="s2">&quot;Simulate Memory Warning&quot;</span>
696</span><span class='line'><span class="k">if</span> <span class="nv">get_language</span><span class="p">()</span> <span class="o">=</span> <span class="s2">&quot;zh_CN&quot;</span> <span class="k">then</span>
697</span><span class='line'>  <span class="k">set</span> <span class="nv">menubar</span> <span class="k">to</span> <span class="s2">&quot;硬件&quot;</span>
698</span><span class='line'>  <span class="k">set</span> <span class="nv">menuclick</span> <span class="k">to</span> <span class="s2">&quot;模拟内存警告&quot;</span>
699</span><span class='line'><span class="k">end</span> <span class="k">if</span>
700</span><span class='line'><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;iPhone Simulator&quot;</span>
701</span><span class='line'>  <span class="nb">activate</span>
702</span><span class='line'><span class="k">end</span> <span class="k">tell</span>
703</span><span class='line'><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;System Events&quot;</span>
704</span><span class='line'>  <span class="k">tell</span> <span class="nv">process</span> <span class="s2">&quot;iPhone Simulator&quot;</span>
705</span><span class='line'>
705      <span class="k">tell</span> <span class="na">menu</span> <span class="nv">bar</span> <span class="mi">1</span>
706</span><span class='line'>          <span class="k">tell</span> <span class="na">menu</span> <span class="nv">bar</span> <span class="nb">item</span> <span class="nv">menubar</span>
707</span><span class='line'>              <span class="k">tell</span> <span class="na">menu</span> <span class="nv">menubar</span>
708</span><span class='line'>                  <span class="nv">click</span> <span class="na">menu</span> <span class="nb">item</span> <span class="nv">menuclick</span>
709</span><span class='line'>              <span class="k">end</span> <span class="k">tell</span>
710</span><span class='line'>          <span class="k">end</span> <span class="k">tell</span>
711</span><span class='line'>      <span class="k">end</span> <span class="k">tell</span>
712</span><span class='line'>  <span class="k">end</span> <span class="k">tell</span>
713</span><span class='line'><span class="k">end</span> <span class="k">tell</span>
714</span></code></pre></td></tr></table></div></figure>
715
716
717<p>保存好文件以后,放入固定的路径,使用UI Automation调用,可以参考一下代码:</p>
718
719<figure class='code'><figcaption><span>JavaScript UI Automation </span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
720<span class='line-number'>2</span>
721<span class='line-number'>3</span>
722<span class='line-number'>4</span>
723<span class='line-number'>5</span>
724<span class='line-number'>6</span>
725<span class='line-number'>7</span>
726<span class='line-number'>8</span>
727<span class='line-number'>9</span>
728<span class='line-number'>10</span>
729</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">simulateMemoryWarning</span><span class="o">:</span> <span class="kd">function</span><span class="p">(){</span>
730</span><span class='line'>    <span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">UIATarget</span><span class="p">.</span><span class="nx">localTarget</span><span class="p">();</span>
731</span><span class='line'>    <span class="kd">var</span> <span class="nx">host</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">host</span><span class="p">();</span>
732</span><span class='line'>    <span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">host</span><span class="p">.</span><span class="nx">performTaskWithPathArgumentsTimeout</span><span class="p">(</span><span class="s2">&quot;/usr/bin/osascript&quot;</span><span class="p">,</span> <span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="nx">scriptPath</span><span class="o">+</span><span class="s2">&quot;SMW.scpt&quot;</span><span class="p">],</span> <span class="mi">5</span><span class="p">);</span>
733</span><span class='line'>    <span class="k">if</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">exitCode</span><span class="o">==</span><span class="mi">0</span><span class="p">){</span>
734</span><span class='line'>        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
735</span><span class='line'>        <span class="p">}</span><span class="k">else</span><span class="p">{</span>
736</span><span class='line'>         <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
737</span><span class='line'>            <span class="p">}</span>
738</span><span class='line'><span class="p">}</span>
739</span></code></pre></td></tr></table></div></figure>
740
741
742<p>好的,功能就做完了。还算顺利,但是如果你是一个geek的话,会觉得之前的AppleScript中有一段很蛋疼的代码。没错我也觉得蛋疼,并且有些老外也觉得蛋疼,所以有一个现成的函数可以使用:</p>
743
744<figure class='code'><figcaption><span>AppleScript use menu_click function</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
745<span class='line-number'>2</span>
746<span class='line-number'>3</span>
747<span class='line-number'>4</span>
748<span class='line-number'>5</span>
749<span class='line-number'>6</span>
750<span class='line-number'>7</span>
751<span class='line-number'>8</span>
752<span class='line-number'>9</span>
753<span class='line-number'>10</span>
754<span class='line-number'>11</span>
755<span class='line-number'>12</span>
756<span class='line-number'>13</span>
757<span class='line-number'>14</span>
758<span class='line-number'>15</span>
759<span class='line-number'>16</span>
760<span class='line-number'>17</span>
761<span class='line-number'>18</span>
762<span class='line-number'>19</span>
763<span class='line-number'>20</span>
764<span class='line-number'>21</span>
765<span class='line-number'>22</span>
766<span class='line-number'>23</span>
767<span class='line-number'>24</span>
768<span class='line-number'>25</span>
769<span class='line-number'>26</span>
770<span class='line-number'>27</span>
771<span class='line-number'>28</span>
772<span class='line-number'>29</span>
773<span class='line-number'>30</span>
774</pre></td><td class='code'><pre><code class='applescript'><span class='line'><span class="k">on</span> <span class="nv">menu_click</span><span class="p">(</span><span class="nv">mList</span><span class="p">)</span>
775</span><span class='line'>  <span class="k">local</span> <span class="nv">appName</span><span class="p">,</span> <span class="nv">topMenu</span><span class="p">,</span> <span class="nv">r</span>
776</span><span class='line'>  <span class="c">-- Validate our input</span>
777</span><span class='line'>  <span class="k">if</span> <span class="nv">mList</span>&#39;s <span class="nv">length</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="k">
777then</span> <span class="k">error</span> <span class="s2">&quot;Menu list is not long enough&quot;</span>
778</span><span class='line'>  <span class="c">-- Set these variables for clarity and brevity later on</span>
779</span><span class='line'>  <span class="k">set</span> <span class="p">{</span><span class="nv">appName</span><span class="p">,</span> <span class="nv">topMenu</span><span class="p">}</span> <span class="k">to</span> <span class="p">(</span><span class="nb">items</span> <span class="mi">1</span> <span class="nb">through</span> <span class="mi">2</span> <span class="k">of</span> <span class="nv">mList</span><span class="p">)</span>
780</span><span class='line'>  <span class="k">set</span> <span class="nv">r</span> <span class="k">to</span> <span class="p">(</span><span class="nb">items</span> <span class="mi">3</span> <span class="nb">through</span> <span class="p">(</span><span class="nv">mList</span>&#39;s <span class="nv">length</span><span class="p">)</span> <span class="k">of</span> <span class="nv">mList</span><span class="p">)</span>
781</span><span class='line'>  <span class="c">-- This overly-long line calls the menu_recurse function with</span>
782</span><span class='line'>  <span class="c">-- two arguments: r, and a reference to the top-level menu</span>
783</span><span class='line'>  <span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;System Events&quot;</span> <span class="k">to</span> <span class="k">my</span> <span class="nv">menu_click_recurse</span><span class="p">(</span><span class="nv">r</span><span class="p">,</span> <span class="p">((</span><span class="nv">process</span> <span class="nv">appName</span><span class="p">)</span>&#39;s <span class="se">¬</span>
784</span><span class='line'>      <span class="p">(</span><span class="na">menu</span> <span class="nv">bar</span> <span class="mi">1</span><span class="p">)</span>&#39;s <span class="p">(</span><span class="na">menu</span> <span class="nv">bar</span> <span class="nb">item</span> <span class="nv">topMenu</span><span class="p">)</span>&#39;s <span class="p">(</span><span class="na">menu</span> <span class="nv">topMenu</span><span class="p">)))</span>
785</span><span class='line'><span class="k">end</span> <span class="nv">menu_click</span>
786</span><span class='line'><span class="k">on</span> <span class="nv">menu_click_recurse</span><span class="p">(</span><span class="nv">mList</span><span class="p">,</span> <span class="nv">parentObject</span><span class="p">)</span>
787</span><span class='line'>  <span class="k">local</span> <span class="nv">f</span><span class="p">,</span> <span class="nv">r</span>
788</span><span class='line'>  <span class="c">-- `f` = first item, `r` = rest of items</span>
789</span><span class='line'>  <span class="k">set</span> <span class="nv">f</span> <span class="k">to</span> <span class="nb">item</span> <span class="mi">1</span> <span class="k">of</span> <span class="nv">mList</span>
790</span><span class='line'>  <span class="k">if</span> <span class="nv">mList</span>&#39;s <span class="nv">length</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">then</span> <span class="k">set</span> <span class="nv">r</span> <span class="k">to</span> <span class="p">(</span><span class="nb">items</span> <span class="mi">2</span> <span class="nb">through</span> <span class="p">(</span><span class="nv">mList</span>&#39;s <span class="nv">length</span><span class="p">)</span> <span class="k">of</span> <span class="nv">mList</span><span class="p">)</span>
791</span><span class='line'>  <span class="c">-- either actually click the menu item, or recurse again</span>
792</span><span class='line'>  <span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;System Events&quot;</span>
793</span><span class='line'>      <span class="k">if</span> <span class="nv">mList</span>&#39;s <span class="nv">length</span> <span class="ow">is</span> <span class="mi">1</span> <span class="k">then</span>
794</span><span class='line'>          <span class="nv">click</span> <span class="nv">parentObject</span>&#39;s <span class="na">menu</span> <span class="nb">item</span> <span class="nv">f</span>
795</span><span class='line'>
795      <span class="k">else</span>
796</span><span class='line'>          <span class="k">my</span> <span class="nv">menu_click_recurse</span><span class="p">(</span><span class="nv">r</span><span class="p">,</span> <span class="p">(</span><span class="nv">parentObject</span>&#39;s <span class="p">(</span><span class="na">menu</span> <span class="nb">item</span> <span class="nv">f</span><span class="p">)</span>&#39;s <span class="p">(</span><span class="na">menu</span> <span class="nv">f</span><span class="p">)))</span>
797</span><span class='line'>      <span class="k">end</span> <span class="k">if</span>
798</span><span class='line'>  <span class="k">end</span> <span class="k">tell</span>
799</span><span class='line'><span class="k">end</span> <span class="nv">menu_click_recurse</span>
800</span><span class='line'><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;iPhone Simulator&quot;</span>
801</span><span class='line'>  <span class="nb">activate</span>
802</span><span class='line'><span class="k">end</span> <span class="k">tell</span>
803</span><span class='line'><span class="nv">menu_click</span><span class="p">({</span><span class="s2">&quot;iPhone Simulator&quot;</span><span class="p">,</span> <span class="s2">&quot;硬件&quot;</span><span class="p">,</span> <span class="s2">&quot;模拟内存警告&quot;</span><span class="p">})</span>
804</span></code></pre></td></tr></table></div></figure>
805
806
807<p>大概玩了一下AppleScript,感觉语法方面有点颠覆之前学过的语言,但是如果能熟练了以后,写代码就像说话一下。感兴趣的同学可以试试。以上相关功能代码已经加入<a href="https://github.com/douban/ynm3k" title="Title">ynm3k</a>中。也可以直接使用的。</p>
808</div>
809  
810  
811
812
813    </article>
814  
815  
816    <article>
817      
818  <header>
819    
820      <h1 class="entry-title"><a href="/blog/octopress.html">才开始写Blog</a></h1>
821    
822    
823      <p class="meta">
824        
825
826
827
828
829
830
831
832
833  
834
835
836<time datetime="2013-05-08T23:01:00+08:00" pubdate data-updated="true">May 8<span>th</span>, 2013</time>
837        
838      </p>
839    
840  </header>
841
842
843  <div class="entry-content"><p>是的,才开始写Blog。之前一直想写,这样可以沉淀一些东西,但是一直没写。</p>
844
845<p>回首工作历程,做测试工程师也10年了,越做越觉得很多东西搞不定,很多问题解决不了。当然,写了Blog也解决不了。</p>
846
847<p>慢慢来吧,一切都会好起来的(深表怀疑 ^_^ )。</p>
848
849<p>本Blog为了标新立异,会遵循以下原则:</p>
850
851<ol>
852<li>实践。有很多实践需要沉淀总结,希望能自己完成积累。可能有很多实践内容有雷同嫌疑,但是可以保证都亲手做过,可以实现。</li>
853<li>吐槽困惑。测试方面存在着太多的槽点,可以吐很多很多。在吐槽时,我会尽可能的保证公正,尽量多的说事实和现象,尽量少的表达观点,写给那些聪明的读者。</li>
854</ol>
855
856</div>
857  
858  
859
860
861    </article>
862  
863  <div class="pagination">
864    
865    <a href="/blog/archives">Blog Archives</a>
866    
867  </div>
868</div>
869<aside class="sidebar">
870  
871    <section>
872  <h1>Recent Posts</h1>
873  <ul id="recent_posts">
874    
875      <li class="post">
876        <a href="/blog/calabash-setup-and-simple-use.html">Calabash Android 初始化和一些使用技巧</a>
877      </li>
878    
879      <li class="post">
880        <a href="/blog/appium-chinese-input.html">Appium中文输入问题的一些探索</a>
881      </li>
882    
883      <li class="post">
884        <a href="/blog/kan-wu.html">《iOS测试指南》勘误一</a>
885      </li>
886    
887      <li class="post">
888        <a href="/blog/ios-testframework.html">这些年用过的iOS测试框架</a>
889      </li>
890    
891      <li class="post">
892        <a href="/blog/applescript-hong-fa-mo-ni-qi-nei-cun-gao-jing.html">UI Automation 中触发iOS模拟器内存警告</a>
893      </li>
894    
895  </ul>
896</section>
897
898<section>
899  <h1>GitHub Repos</h1>
900  <ul id="gh_repos">
901    <li class="loading">Status updating&#8230;</li>
902  </ul>
903  
904  <a href="https://github.com/houlianpi">@houlianpi</a> on GitHub
905  
906  
906<script type="text/javascript">
907    $.domReady(function(){
908        if (!window.jXHR){
909            var jxhr = document.createElement('script');
910            jxhr.type = 'text/javascript';
911            jxhr.src = '/javascripts/libs/jXHR.js';
912            var s = document.getElementsByTagName('script')[0];
913            s.parentNode.insertBefore(jxhr, s);
914        }
915
916        github.showRepos({
917            user: 'houlianpi',
918            count: 4,
919            skip_forks: true,
920            target: '#gh_repos'
921        });
922    });
923  </script>
923
924  
924<script src="/javascripts/github.js" type="text/javascript"> </script>
924
925</section>
926
927
928<section>
929  <ul id="weibo">
930    <li>
931      <iframe width="100%" height="200" class="share_self"  frameborder="0" scrolling="no" src="http://widget.weibo.com/weiboshow/index.php?language=&width=0&height=200&fansRow=2&ptype=1&speed=0&skin=5&isTitle=1&noborder=1&isWeibo=0&isFans=0&uid=2644045772&verifier=eb301345&dpc=1"></iframe>
932    </li>
933  </ul>
934</section>
935<section>
936<div>
937    
937<script type="text/javascript" src="http://www.douban.com/service/badge/mijun/?show=collection&amp;n=2&amp;columns=3" ></script>
937
938</div>
939</section>
940
941
942
943
944  
945</aside>
946
947    </div>
948  </div>
949  <footer role="contentinfo"><p>
950  Copyright &copy; 2015 - mijun(厚脸皮) -
951  <span class="credit">Powered by <a href="http://houlianpi.github.com/">厚脸皮</a></span>
952</p>
953
954</footer>
955  
956
957
958
959
960
961
962
963
964
965
966
967</body>
968</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.