monkeyrunner中的行动并不可靠

时间:2017-03-03 08:34:09

标签: android python monkeyrunner

问题

我的应用程序有一个类似地图的画布,用户可以通过拖动地图来移动它。我想要完成的是将地图移动到100px的右边,然后移动到100px的左边,检查中心的位置是否相同。

代码如下:

device.drag((640, 360), (640 - 100, 360))
device.drag((640, 360), (640 + 100, 360))
# check

这并不是每次都可靠地将地图带到同一个地方。有时设备挂起或慢,它移动几个额外的像素;在最后一步中的其他一些时间它变得更快,给它动力。

有没有办法精确拖动一定数量像素的屏幕?如果设备挂起或速度很慢并不重要,只要最终的移动是正确的。

我的尝试

我试图调整第三个(duration)和第四个(steps)参数无济于事。

我还尝试使用以下方法实现自定义拖动代码:

# Touch down screen                                                                               
device.touch(100, 500, MonkeyDevice.DOWN)                                           

# Move from 100, 500 to 200, 500
for i in range(1, 11):                                                              
    device.touch(100 + 10 * i, 500, MonkeyDevice.MOVE)
    time.sleep(0.1)

# Extra sleep to avoid momentum
time.sleep(0.2)

# Remove finger from screen
device.touch(200, 500, MonkeyDevice.UP)

然后到另一边:

# Touch down screen                                                                               
device.touch(200, 500, MonkeyDevice.DOWN)                                           

# Move from 200, 500 to 100, 500
for i in range(1, 11):                                                              
    device.touch(200 - 10 * i, 500, MonkeyDevice.MOVE)
    time.sleep(0.1)

# Extra sleep to avoid momentum
time.sleep(0.2)

# Remove finger from screen
device.touch(100, 500, MonkeyDevice.UP)

以类似的方式,我也尝试用以下方法测试我的游戏手柄键:

for _ in range(0, 10):
    device.press('KEYCODE_DPAD_RIGHT', MonkeyDevice.DOWN_AND_UP)
    time.sleep(0.1)

for _ in range(0, 10):
    device.press('KEYCODE_DPAD_LEFT', MonkeyDevice.DOWN_AND_UP)
    time.sleep(0.1)

即便如此,有时monkeyrunner会跳过事件或不考虑up事件,从而导致长按(相当于"继续在地图上移动")。

有用的参考文献:

2 个答案:

答案 0 :(得分:1)

非常好的问题。 阅读之后我也很感兴趣,我想知道问题是否也会影响AndroidViewClient/culebra(这更可靠)。

然而,正如下面的步骤可以说明,它可能与地图移动的方式相关,而不是发送的不同协议的可靠性。此外,还提供了自动化测试的方法,正如您所提到的那样,您正在对结果进行视觉比较。

我认为分享此内容会有很大帮助,它可能会为您提供额外的想法来测试您的应用。

首先使用culebra GUI创建测试。

enter image description here

然后我稍微编辑了测试以对点进行分解并添加相反的阻力。

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Main  {
    Student student;
    Hashtable next;
    public Hashtable<String,Student> studentMap;
    public static void main(String[] args){
        Hashtable<String, String> studentMap = new Hashtable<>(10000, 0.75f);
        studentMap.keySet().forEach((key) -> {
            String value = studentMap.get(key);
            System.out.println("Key = " + key + ", Value = " + value);
        });   
        //adding values to array
        studentMap.put("16012804", "Jennifer");
        studentMap.put("13747732", "Beatrice");
        studentMap.put("14058392", "Bob");
        Set set = studentMap.entrySet();
        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            Map.Entry mapentry = (Map.Entry)iterator.next();
            System.out.print("key is: "+ mapentry.getKey() + " & Value is: ");
            System.out.println(mapentry.getValue());
        }
        //Get values based on key
        String var= studentMap.get("16012804");
        System.out.println("Value at index 1 is: "+var);
        // Remove values based on key
        studentMap.remove("16012804");
        System.out.println("Map key and values after removal:");
        Set set2 = studentMap.entrySet();
        Iterator iterator2 = set2.iterator();
        while(iterator2.hasNext()) {
          Map.Entry mapentry2 = (Map.Entry)iterator2.next();
          System.out.print("Key is: "+mapentry2.getKey() + " & Value is: ");
          System.out.println(mapentry2.getValue());
        }
        Set keyset = studentMap.keySet();
        System.out.println("Key set values are:" + keyset);
        boolean val = studentMap.isEmpty();
        System.out.println("Is hash map empty: " + val);
        System.out.println("Size of the Hashtable " + studentMap.size());
        System.out.println("Load Factor: " + loadFactor(studentMap));
        System.out.println("Hash: " + studentMap.hashCode());
        System.out.println("Size of map is now: " +  mapcapacity(studentMap));


    }
    public static float loadFactor(Map studentMap){
    int count=0;
    float load;
    for(int i=0; i<studentMap.size(); i++){
            count += studentMap.size();
    }
    load = count/(float)studentMap.size();
    return load;
    }
    //if the size of the map is greater than the map capacity * load factor - then double the size of map. 
    public static Integer mapcapacity(Map studentMap){
       Integer initCapacity=2;
       float loadFactor=0.75f;
       boolean capacityFound=false;
        Integer capacity=initCapacity;
        Integer size=studentMap.size();
        while(!capacityFound){
            if(size>capacity*loadFactor){
                capacity=capacity*2;

            }
            else {
                capacityFound=true;

            }
        }
        return capacity;

    }
    public int hash(String key){
       return (Math.abs(key.hashCode())) % studentMap.size();  
    }



}

一旦运行,这些是迭代之前和之后的结果

enter image description here enter image description here enter image description here

在最后一张图片中,您可以看到视觉比较,并显示出轻微的移动。但是,我认为这可能就像地图一样。

我还使用CulebraTester,它使用基于UiAutomator的完全不同的后端,同样的问题出现了。所以我认为与#! /usr/bin/env python # -*- coding: utf-8 -*- ''' Copyright (C) 2013-2016 Diego Torres Milano Created on 2017-03-04 by Culebra __ __ __ __ / \ / \ / \ / \ ____________________/ __\/ __\/ __\/ __\_____________________________ ___________________/ /__/ /__/ /__/ /________________________________ | / \ / \ / \ / \ \___ |/ \_/ \_/ \_/ \ o \ \_____/--< @author: Diego Torres Milano @author: Jennifer E. Swofford (ascii art snake) ''' import re import sys import os import unittest try: sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src')) except: pass import pkg_resources pkg_resources.require('androidviewclient>=13.0.0') from com.dtmilano.android.viewclient import ViewClient, CulebraTestCase from com.dtmilano.android.uiautomator.uiautomatorhelper import UiAutomatorHelper, UiScrollable, UiObject, UiObject2 TAG = 'CULEBRA' class CulebraTests(CulebraTestCase): @classmethod def setUpClass(cls): cls.kwargs1 = {'ignoreversioncheck': False, 'verbose': False, 'ignoresecuredevice': False} cls.kwargs2 = {'forceviewserveruse': False, 'useuiautomatorhelper': False, 'ignoreuiautomatorkilled': True, 'autodump': False, 'startviewserver': True, 'compresseddump': True} cls.options = {'start-activity': None, 'concertina': False, 'device-art': None, 'use-jar': False, 'multi-device': False, 'unit-test-class': True, 'save-screenshot': None, 'use-dictionary': False, 'glare': False, 'dictionary-keys-from': 'id', 'scale': 1, 'find-views-with-content-description': True, 'window': -1, 'orientation-locked': None, 'save-view-screenshots': None, 'find-views-by-id': True, 'log-actions': False, 'use-regexps': False, 'null-back-end': False, 'auto-regexps': None, 'do-not-verify-screen-dump': True, 'verbose-comments': False, 'gui': False, 'find-views-with-text': True, 'prepend-to-sys-path': False, 'install-apk': None, 'drop-shadow': False, 'output': None, 'unit-test-method': None, 'interactive': False} cls.sleep = 5 def setUp(self): super(CulebraTests, self).setUp() def tearDown(self): super(CulebraTests, self).tearDown() def preconditions(self): if not super(CulebraTests, self).preconditions(): return False return True def testSomething(self): if not self.preconditions(): self.fail('Preconditions failed') _s = CulebraTests.sleep _v = CulebraTests.verbose d = '/tmp/' p = (377.14, 380.86) q = (175.14, 380.86) self.vc.writeImageToFile(d + 'map-start.png') # Each step execution is throttled to 5ms per step # So for a 400 steps, the swipe will take about 2 second to complete steps = 400 duration = 2000 # Let's give some extra delay. sleep = 3 for n in range(10): print n self.device.dragDip(p, q, duration, steps) self.vc.sleep(sleep) self.device.dragDip(q, p, duration, steps) self.vc.sleep(sleep) self.vc.writeImageToFile(d + 'map-finish.png') self.device.compare(d + 'map-finish.png', d + 'map-start.png', d + 'map-compare.png') if __name__ == '__main__': CulebraTests.main() 无关。

答案 1 :(得分:1)

我已经用我们的appetizer-toolkit测量了视觉差异,并在此处发现了类似的问题。只是一些想法和以前的经验。

对于时间安排,我们尝试了几个后端,例如sendeventMonkeyDevice / Chimp。正如我所提到的,sendevent将为每个点创建一个进程,这完全杀死了时间。设备上的MonkeyDevice代理不是那么可靠,我们有一些初步尝试但后来被放弃了。如果您有兴趣,请查看this。当前可接受的后端是openstf/minitouch,它与当前工具包一起使用。在校准和调整之后,我相信工具包会变得更好(目前针对1.0.5发布)

我怀疑无论用什么输入录音和重放工具,都可以实现真正的“确定性重放”。根据我们的经验,即使我们可以对输入事件(x,y和时间)强制执行完美的确定性,应用程序仍然会有其他非确定性,特别是滞后,网络活动和Canvas视图响应。我怀疑你遇到的问题是MonkeyDevice的不精确和Canvas View问题的加起来。因此,对于更高级别的设计,将这些工具以及我们的工具包视为“输入自动化工具”,并对错误有一定的容忍度。