Macaca Macaca Android 和 iOS 并行测试

tongshanshanshan · 发布于 2016年12月20日 · 最后由 tfzhou 回复于 2017年1月17日 · 1865 次阅读
本帖已被设为精华帖!

前言

从appium 迁移到了macaca,研究了一下多机并行跑case,总结一下,和大家一起讨论一下~~~

1. 获取连接到电脑上的机器

  • Android :利用 adb devices 获取连接上的所有安卓设备。
  • iOS:利用 instruments -s devices 获取连接上的所有iOS设备

    class InitDevice:
    """
    获取连接的设备的信息
    """
    def __init__(self):
        self.GET_ANDROID = "adb devices"
        self.GET_IOS = "instruments -s devices"
    
    def get_device(self):
        value = os.popen(self.GET_ANDROID)
    
        device = []
    
        for v in value.readlines():
            android = {}
            s_value = str(v).replace("\n", "").replace("\t", "")
            if s_value.rfind('device') != -1 and (not s_value.startswith("List")) and s_value != "":
                android['platformName'] = 'Android'
                android['udid'] = s_value[:s_value.find('device')].strip()
                android['package'] = 'xxxx'
                android['activity'] = 'xxxxxx'
                device.append(android)
    
        value = os.popen(self.GET_IOS)
    
        for v in value.readlines():
            iOS = {}
    
            s_value = str(v).replace("\n", "").replace("\t", "").replace(" ", "")
    
            if v.rfind('Simulator') != -1:
                continue
            if v.rfind("(") == -1:
                continue
    
            iOS['platformName'] = 'iOS'
            iOS['platformVersion'] = re.compile(r'\((.*)\)').findall(s_value)[0]
            iOS['deviceName'] = re.compile(r'(.*)\(').findall(s_value)[0]
            iOS['udid'] = re.compile(r'\[(.*?)\]').findall(s_value)[0]
            iOS['bundleId'] = 'xxxx'
    
            device.append(iOS)
    
        return device
    

    2. 动态获取3456 端口后的空闲端口,获取的端口数根据上个方法中获取的设备数

  • 判断端口号是否被占用是去执行 netstat -an | grep port这条命令判断端口号是否被占用

def is_using(port):
    """
    判断端口号是否被占用
    :param port:
    :return:
    """
    cmd = "netstat -an | grep %s" % port

    if os.popen(cmd).readlines():
        return True
    else:
        return False

def get_port(count):
    """
    获得3456端口后一系列free port
    :param count:
    :return:
    """
    port = 3456
    port_list = []
    while True:
        if len(port_list) == count:
            break

        if not is_using(port) and (port not in port_list):
            port_list.append(port)
        else:
            port += 1

    return port_list

3. 开启macaca服务,为每一个service动态分布一个端口

  • start_server: 开启一个进程池,每一个device对应一个macaca server
  • run_server:运行macaca server
  • is_running:判断server是否有开启成功,判断的方法为:去访问每个server对应的http://127.0.0.1:port/wd/hub/status地址,看看返回的状态码是不是以2 开头。
  • run_test:运行脚本
class macacaServer():
    def __init__(self, devices):

        self.devices = devices
        self.count = len(devices)
        self.url = 'http://127.0.0.1:%s/wd/hub/status'

    def start_server(self):

        pool = Pool(processes=self.count)
        port_list = get_port(self.count)

        for i in range(self.count):
            pool.apply_async(self.run_server, args=(self.devices[i], port_list[i]))

        pool.close()
        pool.join()

    def run_server(self, device, port):

        r = RunServer(port)
        r.start()

        while not self.is_running(port):
            sleep(1)

        server_url = {
            'hostname': "ununtrium.local",
            'port': port,
        }
        driver = WebDriver(device, server_url)
        driver.init()

        DRIVER.set_driver(driver)
        DRIVER.set_OS(device.get("platformName"))

        self.run_test()

    def run_test(self):
        """运行测试
        """
        all_test = AllTests()
        all_test.run_case()

    def is_running(self, port):
        """Determine whether server is running
        :return:True or False
        """
        url = self.url % port
        response = None
        try:
            response = requests.get(url, timeout=0.01)

            if str(response.status_code).startswith('2'):

                # data = json.loads((response.content).decode("utf-8"))

                # if data.get("staus") == 0:
                return True

            return False
        except requests.exceptions.ConnectionError:
            return False
        except ReadTimeout:
            return False
        finally:
            if response:
                response.close()


class RunServer(threading.Thread):

    def __init__(self, port):
        threading.Thread.__init__(self)
        self.cmd = 'macaca server -p %s --verbose' % port

    def run(self):
        os.system(self.cmd)

最后

本来想放个视频看看效果的,但是发现不会搞~~~ 我还是早点睡觉吧。。。。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 57 条回复
110 Lihuazhang 将本帖设为了精华贴 12月21日 00:20
110 Lihuazhang 将本帖设为了精华贴 12月21日 00:20
648
xinxjxjxj · #3 · 2016年12月21日

又有新的东西可以学习了,赞

6504
lose · #4 · 2016年12月21日

这个厉害~有点意思,哈哈

96
lingcizhisheng · #5 · 2016年12月21日

mark,这个确实可以,干货

12574
AllocAndInit · #6 · 2016年12月22日

我想在真机上进行iOS的并行测试,然后我启动了两个终端窗口,分别去执行相应的测试用例,为什么会出现两个测试用例的运行日志只在一个终端窗口输出?并且当其中一个测试用例执行完毕以后,另外一个测试用例如果即使只执行到一半,马上也会跟着一起被终止?请问这是什么情况了?

5338
tongshanshanshan · #7 · 2016年12月22日

#6楼 @AllocAndInit 启动的时候有没有指定端口,测试运行的时候有没有指定相应的端口?你这样问我只能猜,能不能上脚本和相关log?

104
seveniruby · #8 · 2016年12月22日

请更新微信或者微信二维码. testerhomer要打赏

5338
tongshanshanshan · #9 · 2016年12月23日

#8楼 @seveniruby 已更新

11362
Tank007 · #10 · 2016年12月28日
def run_server(self, device, port):

       r = RunServer(port)
       r.start()

       while not self.is_running(port):
           sleep(1)

       server_url = {
           'hostname': "ununtrium.local",
           'port': port,
       }
       driver = WebDriver(device, server_url)
       driver.init()

       DRIVER.set_driver(driver)
       DRIVER.set_OS(device.get("platformName"))

       self.run_test()

楼主,你的 DRIVER.set_driver(driver),driver是怎么传递的?这个地方能详细解释一下吗?

5338
tongshanshanshan · #11 · 2016年12月28日

#10楼 @Tank007 我是定义了一个DRIVER类,具体内容为

class DRIVER:

    driver = None
    OS = None

    @classmethod
    def set_driver(cls, driver):
        cls.driver = driver

    @classmethod
    def set_OS(cls, OS):
        cls.OS = OS

在case中这样获取 DRIVER.driver

11362
Tank007 · #12 · 2016年12月28日

#11楼 @tongshanshanshan 嗯,差不多啊,我也是这样用的,

class DRIVER:

    driver = None

传值的时候,DRIVER.driver = driver(后面的driver为获取到的),那么,driver到这里后,api调用的时候呢?

5338
tongshanshanshan · #13 · 2016年12月28日

#12楼 @Tank007 case刚开始的时候,获取driver = DRIVER.driver, 后面使用的时候,可以直接driver.find_element_by_id()这样使用。

11362
Tank007 · #14 · 2016年12月28日

#13楼 @tongshanshanshan 这个肯定是没问题的,其实我想知道的是,比如我进行api 的一些二次封装,我肯定要传driver进来的,我现在遇到的问题是,比如我的二次封装类为 Element,初始化的时候self.driver = DRIVER.driver,那么会取到set_driver 传进来的driver么?

5338
tongshanshanshan · #15 · 2016年12月28日

#14楼 @Tank007 会,在你封装的类里面直接取DRIVER.driver就可以了,这里取的就是你设置的driver。

11362
Tank007 · #16 · 2016年12月28日

#15楼 @tongshanshanshan 我现在的做法是,直接在Element这里取driver,其他地方不再取了,不过还没驱动起来,😂 ,既然确定了设计思路没问题,那就再试试,多谢楼主,我再调试一下,实在不行,再跟你请教

7555
harsayer · #17 · 2016年12月30日

@tongshanshanshan 你好,咨询下,有点没看懂你这么做的含义.意思是 你这套py脚本 是跑在一个 "测试执行服务环境" 的吗?
那么 这个"测试执行服务环境" 是一个什么平台下的环境 是支持android APP测试的 环境 还是支持ios APP测试的环境?

这个测试执行服务环境 不可能既是android又是ios的吧?

还是 你这个脚本 是去两套测试执行环境下去分别驱动 andriod 和ios的 否则 怎么叫 Android 和 iOS 并行测试.

这个并行 是在一套测试环境下的 还是 分别两套安卓和苹果环境下的?

5338
tongshanshanshan · #18 · 2016年12月30日

#17楼 @harsayer 不好意思啊,你这里的 “测试执行服务环境”指的是什么?我不太明白哎。。我是在一台电脑上同时根据不同的端口开启了多个macaca server。

96
sailorchen · #19 · 2016年12月30日

楼主可以给个windows,安卓版本用的软件么?Node.js和需要Node.js安装的东西

7555
harsayer · #20 · 2016年12月30日

#18楼 @tongshanshanshan 就是跑用例的自动化测试服务环境,我们目前就是一台电脑ubuntu16.04,部署了macaca-cli和macaca-android还有就是android-sdk执行环境,只能跑安卓app的用例。要是跑ios app的话那不是得mac系统环境吗?你这套脚本可以同时一个环境下去检测连接到这个环境的ios和android?同时在这个环境下去并行安卓和苹果app用例?

5338
tongshanshanshan · #21 · 2016年12月30日

是的,这可以的。但是我的电脑是mac。

7555
harsayer · #22 · 2017年1月03日

#21楼 @tongshanshanshan 看来自动化测试服务器 还是要一步到位弄个mac机 这样既可以连ios机又可以连android机....

96
zyyuyu123 · #23 · 2017年1月06日
pool.apply_async(self.run_server, args=(self.devices[i], port_list[i])

class RunServer(threading.Thread):

    def __init__(self, port):
        threading.Thread.__init__(self)
        self.cmd = 'macaca server -p %s --verbose' % port

    def run(self):
        os.system(self.cmd)

这两段启动 macaca server port 的代码,我运行没有成功,apply_async 运行时根本没有调用 run_server方法。还有 macaca server -p %s --verbose 是否需要守护进程去调用,服务会一直保持启动,apply_async不会回到主进程?

5338
tongshanshanshan · #24 · 2017年1月09日

#23楼 @zyyuyu123

  • apply_async 运行时根本没有调用 run_server方法 没有报错么?如果是直接结束了 ,可以定义一个result,将报错信息打印出来
result = []
result.append(pool.apply_async(self.run_server, args=(self.devices[i], port_list[i]))
for res in result:
        print(res.get())
  • macaca server -p %s --verbose 是否需要守护进程去调用,服务会一直保持启动,apply_async不会回到主进程 会一直保持启动。会回到主进程
11362
Tank007 · #25 · 2017年1月10日

#15楼 @tongshanshanshan

def run_server(self, device, port):

        r = RunServer(port)
        r.start()

        while not self.is_running(port):
            sleep(1)

        server_url = {
            'hostname': "ununtrium.local",
            'port': port,
        }
        driver = WebDriver(device, server_url)
        driver.init()

        DRIVER.set_driver(driver)
        DRIVER.set_OS(device.get("platformName"))

        self.run_test()

在这个方法里面,你是如何实现的

driver = WebDriver(device, server_url)
driver.init()

我现在遇到的问题是,pool.apply_async(self.run, (self.devices[i], pool_list[i])) 并行执行不了,一运行就结束了,换了种方法

threadpool = []
    for i in range(self.count):
        t = RunThread(self.devices[i],pool_list[i])
        threadpool.append(t)

    for j in threadpool:
        j.start()

    for k in threadpool:
        k.join()

现在手机和模拟器的WDA都能自动build上去,但是运行用例的时候,只能跑一台机器,有一个会 [ ERROR ] Message: Session does not exist ,请问是哪里用错了么?

5338
tongshanshanshan · #26 · 2017年1月10日

#25楼 @Tank007

driver = WebDriver(device, server_url)
driver.init()

这个方法不是我实现的,是macaca自己封装好,用来初始化driver的。

pool.apply_async(self.run, (self.devices[i], pool_list[i])) 并行执行不了,一运行就结束了

这个是因为在在执行self.run这个方法的过程中报错了,所以提前结束。但是并没有把报错信息给打印出来。你可以添加以下代码,就可以看见报错信息了。

result = []
result.append(pool.apply_async(self.run_server, args=(self.devices[i], port_list[i]))
for res in result:
        print(res.get())

至于最后一个 [ ERROR ] Message: Session does not exist:
你确定所有服务都起来之后才运行用例的么?

11362
Tank007 · #27 · 2017年1月10日

#26楼 @tongshanshanshan 现在改用pool.apply_async,我把进程id也给打印出来,发现两次的进程id居然是一样的,不科学啊。

result = []
        for i in range(self.count):
            print 'device = %s,port: %s' % (self.devices[i],str(pool_list[i]))
            print 'Run task %s (%s)...' % (i, os.getpid())
            res_pool = pool.apply_async(self.run, (self.devices[i], pool_list[i]))
            result.append(res_pool)

        pool.close()
        pool.join()
        print 'All subprocesses done.'
        for res in result:
            print res.get

日志

进程数:  2
端口列表:  [4723, 4724]
Run task 0 (59622)...
Run task 1 (59622)...
All subprocesses done.
<bound method ApplyResult.get of <multiprocessing.pool.ApplyResult object at 0x1059bc310>>
<bound method ApplyResult.get of <multiprocessing.pool.ApplyResult object at 0x1059bc390>>
appium 相关进程列表 = ['59652\n'] 
CleanProcess:Darwin:kill appium

5338
tongshanshanshan · #28 · 2017年1月10日

#27楼 @Tank007 你这样打印当然就是一个进程了,你这里打印出来的都是主进程的pid。去self.run里面打印。

11362
Tank007 · #29 · 2017年1月10日

#28楼 @tongshanshanshan 确实哦,没注意看,现在把打印加入到run里面了,但是没有日志输出

进程数:  2
端口列表:  [4723, 4724]
All subprocesses done.
<bound method ApplyResult.get of <multiprocessing.pool.ApplyResult object at 0x10bc72310>>
<bound method ApplyResult.get of <multiprocessing.pool.ApplyResult object at 0x10bc72390>>
appium 相关进程列表 = ['60663\n'] 
CleanProcess:Darwin:kill appium
5338
tongshanshanshan · #30 · 2017年1月10日

#29楼 @Tank007
print 'All subprocesses done.'
for res in result:
print res.get

这个也没有打印出来么?
还有啊,是print res.get()

11362
Tank007 · #31 · 2017年1月10日

#30楼 @tongshanshanshan 你看上面的日志,有打印啊,print res.get()这样用会报错“cPickle.PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup builtin.instancemethod failed”

11362
Tank007 · #32 · 2017年1月12日

#30楼 @tongshanshanshan 还是得请教你,那个pid的问题已经解决了。现在问题好奇怪,我一台模拟器,一台手机,结果设置参数串了,目前单独跑都没问题,一并行跑,就只能跑一台,但是两台的wda都能build上,其中真机系统版本10.0.2,模拟器10.2,但是真机日志中发现出现了10.2的系统版本,有点懵了,你能看出问题么?
代码:


def Run_one(device,port):
    print 'Run task %s (%s) at %s' % (str(port), os.getpid(), time.ctime())
    S.set_device(device)

    from src.lib.Log import LogSignleton
    logsignleton = LogSignleton(device)
    logger = logsignleton.logger
    L.set_logger(logger)
    print '*' * 80
    print time.ctime(),' [', __name__, '::', Run_one.__name__, '] :', ' logger =  ', logger

    # 启动appium 服务
    appium_server.start_server(device, port)

    # 实例化Dirver
    Dr = Driver(device, port)
    Dr.init()  # 初始化driver
    driver = Dr.getDriver()
    D.set_driver(driver)
    print '*' * 80
    print time.ctime(),' [', __name__, '::', Run_one.__name__, '] :', ' driver =  ', driver

    # 生成runner
    runner = unittest.TextTestRunner()
    # 循环遍历测试用例列表
    for case_list in case_sheet1[1:]:
        test_suite = __get_test_suite(case_list)
        runner.run(test_suite)

    # 生成测试报告
    RunExcel.get_html_report()

    # run_mode() # 运行模式

if __name__ == '__main__':
    appium_server = AppiumServer()
    devices = appium_server.get_device()
    count = len(devices)
    ports = appium_server.get_port(count)
    print 'Run task pid: (%s) at: %s' % ( os.getpid(), time.ctime())
    print '*'*80
    p = Pool(count)
    for i in range(count):
        p.apply_async(Run_one,(devices[i],ports[i],))

    p.close()
    p.join()
    print 'All finished'
真机
appium -a 127.0.0.1 -p 4724 -U bcf1530f7fae062a4433b658b75f4bde43aaf15f

[debug] [XCUITest] Waiting up to 60000ms for WebDriverAgent to start
[debug] [XCUITest] Log file for xcodebuild test: /Users/zengyuanchen/Library/Developer/Xcode/DerivedData/WebDriverAgent-fuwmnzdhnozghbgmpkthbcubvqlr/Logs/Test/0F3BAEF0-6655-49DC-9B93-A987D6305E75/Session-WebDriverAgentRunner-2017-01-12_114216-Yga1Hy.log
[debug] [XCUITest] WebDriverAgent successfully started after 14389ms
[debug] [iProxy] recv failed: Resource temporarily unavailable
[debug] [iProxy] recv failed: Operation not permitted
[debug] [XCUITest] Sending createSession command to WDA
[debug] [JSONWP Proxy] Proxying [POST /session] to [POST http://localhost:8100/session] with body: {"desiredCapabilities":{"bundleId":"rym.pingan.rympush","arguments":[],"environment":{},"shouldWaitForQuiescence":true}}
[debug] [JSONWP Proxy] Got response with status 200: {"value":{"sessionId":"9DDA03DA-857B-4768-957D-C81CD9B1C2A4","capabilities":{"device":"iphone","browserName":"任意门","sdkVersion":"10.0.2","CFBundleIdentifier":"rym.pingan.rympush"}},"sessionId":"9DDA03DA-857B-4768-957D-C81CD9B1C2A4","status":0}
[debug] [XCUITest] Setting initial orientation to 'PORTRAIT'
[debug] [JSONWP Proxy] Proxying [POST /orientation] to [POST http://localhost:8100/session/9DDA03DA-857B-4768-957D-C81CD9B1C2A4/orientation] with body: {"orientation":"PORTRAIT"}
[debug] [iProxy] recv failed: Resource temporarily unavailable
[debug] [iProxy] recv failed: Operation not permitted
[debug] [JSONWP Proxy] Got response with status 200: {"value":{},"sessionId":"9DDA03DA-857B-4768-957D-C81CD9B1C2A4","status":0}
[Appium] New XCUITestDriver session created successfully, session 23b7a53e-9784-43c8-8b25-f9b6db07c151 added to master session list
[debug] [MJSONWP] Responding to client with driver.createSession() result: {"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"","platform":"MAC","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"deviceName":"Rym'iPhone","udid":"bcf1530f7fae062a4433b658b75f4bde43aaf15f","autoAcceptAlerts":true,"automationName":"XCUITest","noReset":true,"WaitForAppScript":true,"platformVersion":"10.2","platformName":"iOS","bundleId":"rym.pingan.rympush"}
[HTTP] <-- POST /wd/hub/session 200 21123 ms - 514 

下面是模拟器

模拟器
appium -a 127.0.0.1 -p 4723 -U 32E6D124-29B6-48A2-9B38-0A9D54121E10  

[debug] [XCUITest] WebDriverAgent successfully started after 829ms
[debug] [XCUITest] Sending createSession command to WDA
[debug] [JSONWP Proxy] Proxying [POST /session] to [POST http://localhost:8100/session] with body: {"desiredCapabilities":{"bundleId":"rym.pingan.rympush","arguments":[],"environment":{},"shouldWaitForQuiescence":true}}
[debug] [JSONWP Proxy] Got response with status 200: {"value":{"sessionId":"A85E029D-38D3-407F-96E0-44020D1279C7","capabilities":{"device":"iphone","browserName":"任意门","sdkVersion":"10.0.2","CFBundleIdentifier":"rym.pingan.rympush"}},"sessionId":"A85E029D-38D3-407F-96E0-44020D1279C7","status":0}
[debug] [XCUITest] Setting initial orientation to 'PORTRAIT'
[debug] [JSONWP Proxy] Proxying [POST /orientation] to [POST http://localhost:8100/session/A85E029D-38D3-407F-96E0-44020D1279C7/orientation] with body: {"orientation":"PORTRAIT"}
[debug] [JSONWP Proxy] Got response with status 200: {"value":{},"sessionId":"A85E029D-38D3-407F-96E0-44020D1279C7","status":0}
[Appium] New XCUITestDriver session created successfully, session 128bc2f7-3c59-4495-a9b5-e77cf62e5565 added to master session list
[debug] [MJSONWP] Responding to client with driver.createSession() result: {"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"","platform":"MAC","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"deviceName":"iPhone6s","udid":"32E6D124-29B6-48A2-9B38-0A9D54121E10","autoAcceptAlerts":true,"automationName":"XCUITest","noReset":true,"WaitForAppScript":true,"platformVersion":"10.2","platformName":"iOS","bundleId":"rym.pingan.rympush"}
[HTTP] <-- POST /wd/hub/session 200 22330 ms - 508 
5338
tongshanshanshan · #33 · 2017年1月12日

#32楼 @Tank007
你好,我可以看看你的devices = appium_server.get_device()这个的devices的具体代码么?

11362
Tank007 · #34 · 2017年1月12日

#33楼 @tongshanshanshan 这个方法基本是按照你的样例写的

def get_device(self):
    '''
    获取真实设备列表
    :param deviceName:
    :param deviceType:
    :return:
    '''
    # 存储设备信息
    device = []
    # value_ios = os.popen(self.Get_iOS)
    isMonitor = eval(read_config('appium', 'isMonitor'))
    # 获取模拟器设备列表
    if isMonitor:
        iOS_Monitor = {}
        deviceName = read_config('appium', 'deviceName')
        platformVersion = read_config('appium', 'platformVersion')
        iOS_Monitor['deviceName'] = deviceName
        # deviceName = str(deviceName).replace(' ','')
        for dev in os.popen(self.Get_iOS).readlines():
            dev_value = str(dev).replace("\n", "").replace("\t", "").replace(" ", "")
            # print dev_value
            if dev_value.rfind(deviceName) == -1:
                continue
            if dev_value.rfind(platformVersion) == -1:
                continue
            # print dev_value
            re_deviceName = re.compile(r'(.*)\(').findall(dev_value)[0]
            re_deviceName2 = re.compile(r'(.*)\(').findall(re_deviceName)[0]
            # print 're_deviceName2: ' , re_deviceName2
            # print 'deviceName: ',deviceName
            if re_deviceName2 != deviceName:
                continue
            # if re_deviceName != deviceName:
            #   continue
            iOS_Monitor['udid'] = re.compile(r'\[(.*?)\]').findall(dev_value)[0]
            iOS_Monitor['platformVersion'] = platformVersion
            iOS_Monitor['platformName'] = 'iOS'
        device.append(iOS_Monitor)
    else:
        pass
    # 是否运行真机
    isRealDevice = eval(read_config('appium', 'isRealDevice'))
    if isRealDevice:
        # 获取iOS真实设备列表
        for dev in os.popen(self.Get_iOS).readlines():
            iOS = {}
            Android = {}
            dev_value = str(dev).replace("\n", "").replace("\t", "").replace(" ", "")
            if dev.rfind('Simulator') != -1:
                continue
            if dev.rfind('(') == -1:
                continue
            # print dev_value
            # iOS['platformName'] = read_config('appium', 'platformName')
            # iOS['bundleId'] = read_config('appium','bundleId')
            iOS['deviceName'] = re.compile(r'(.*)\(').findall(dev_value)[0]
            iOS['platformVersion'] = re.compile(r'\((.*)\)').findall(dev_value)[0]
            iOS['udid'] = re.compile(r'\[(.*?)\]').findall(dev_value)[0]
            iOS['platformName'] = 'iOS'

            device.append(iOS)
        # print iOS

        # 命令获取Android设备列表
        for dev in os.popen(self.Get_Android).readlines():
            dev_value = str(dev).replace("\n", "").replace("\t", "")
            if dev_value.rfind('device') != -1 and (not dev_value.startswith("List")) and dev_value != "":
                Android['udid'] = dev_value[:dev_value.find('device')].strip()
                Android['platformName'] = 'Android'
                device.append(Android)
    else:
        pass

    print time.ctime(), ' [', __name__, '::', AppiumServer.get_device.__name__, '] :', ' device =  ', device
    return device

11362
Tank007 · #35 · 2017年1月12日

#33楼 @tongshanshanshan 这个方法应该是没什么问题,获取的信息也是对的,单独调试这个方法可以获取到正确的设备信息。从我调试的结果来看,driver初始化的时候,这里的日期打印时间,两个设备不是同一个时间,其他包括启动appium的服务,获取设备信息都是同一个时间(差几秒),所以,是不是driver这里有问题?

11362
Tank007 · #36 · 2017年1月12日

@tongshanshanshan 而且更奇葩的是,我出现过不少次,感觉线程堵塞了,就是设备用例跑不起来,杀掉node都不行,需要重启电脑才可以,还有把我5s手机给跑的发烫,然后卡的要死,也要重启才会好。从开始弄多线程,都快疯了都。。。

5338
tongshanshanshan · #37 · 2017年1月12日

#36楼 @Tank007 这边啊
[debug] [JSONWP Proxy] Proxying [POST /session] to [POST http://localhost:8100/session] with b 为啥是都是8100 端口,你这里不是设置的端口号是4724么?

11362
Tank007 · #38 · 2017年1月12日

#37楼 @tongshanshanshan 我也不知道啊,这个是wda的默认端口,会不会和appium的端口没有关系?单独跑的时候也没见跑不了啊

5338
tongshanshanshan · #39 · 2017年1月12日

#38楼 @Tank007 那你可以修改wda的端口么? 2个分别用不同的端口呢。

11362
Tank007 · #40 · 2017年1月12日

#39楼 @tongshanshanshan 你那边运行的时候,有这种端口问题么?用macaca的时候

5338
tongshanshanshan · #41 · 2017年1月12日

没有,我只要设置macaca server运行的端口就行了。

11362
Tank007 · #42 · 2017年1月12日

#41楼 @tongshanshanshan 一个道理啊,我也是只要设置appium运行的端口就可以了,不应该啊,macaca也调用wda的,也有默认端口的呀

5338
tongshanshanshan · #43 · 2017年1月12日

#42楼 @Tank007 我刚刚运行了一下,发现用的是2个不同的端口。

>> xctest-client.js:56:14 [master] pid:10556 project path: /usr/local/lib/node_modules/macaca-ios/node_modules/webdriveragent/WebDriverAgent/WebDriverAgent.xcodeproj
>> xctest-client.js:56:14 [master] pid:10555 project path: /usr/local/lib/node_modules/macaca-ios/node_modules/webdriveragent/WebDriverAgent/WebDriverAgent.xcodeproj
>> macaca-ios.js:147:10 [master] pid:10556 {
    "bundleId": "com.btcc.mobiEntStaging",
    "platformVersion": "9.3.5",
    "platformName": "iOS"
}
>> macaca-ios.js:147:10 [master] pid:10555 {
    "bundleId": "com.btcc.mobiEntStaging",
    "platformVersion": "10.0.1",
    "platformName": "iOS"
}
>> macaca-ios.js:151:12 [master] pid:10556 Trying to start wda server...
>> macaca-ios.js:151:12 [master] pid:10555 Trying to start wda server...
(node:10556) DeprecationWarning: Calling an asynchronous function without callback is deprecated.
(node:10555) DeprecationWarning: Calling an asynchronous function without callback is deprecated.
(node:10555) DeprecationWarning: Calling an asynchronous function without callback is deprecated.
(node:10556) DeprecationWarning: Calling an asynchronous function without callback is deprecated.
BundleId com.apple.test.WebDriverAgentRunner-Runner does not exist.
>> xctest-client start with port: 8900
>> xctest-client.js:233:14 [master] pid:10556 xcode version: 8.2
>> WebDriverAgent version: 1.0.38
>> xctest-client start with port: 8901
>> xctest-client.js:233:14 [master] pid:10555 xcode version: 8.2
>> WebDriverAgent version: 1.0.38
5338
tongshanshanshan · #44 · 2017年1月12日

#42楼 @Tank007
你这个是并行的log吧,如果是,我感觉就是这个问题。

11362
Tank007 · #45 · 2017年1月12日

#43楼 @tongshanshanshan 嗯,那么问题来了,我怀疑是我这边用的是同一个端口,我再仔细看看

11362
Tank007 · #46 · 2017年1月12日

@tongshanshanshan 这两个设备用的wda的同一个端口进行通信,都是8100,是这个问题么?

11362
Tank007 · #47 · 2017年1月12日

#44楼 @tongshanshanshan 是并行的log,我分别手动开了两个appium的服务,然后分别截取的日志

11362
Tank007 · #48 · 2017年1月12日

@seveniruby 请教一个问题,多台设备并发使用appium的服务,所有设备都使用一个端口(8100),还是使用不同的端口,macaca是用不同的端口耶

5338
tongshanshanshan · #49 · 2017年1月12日

#48楼 @Tank007 我觉得应该是使用不同的端口。appium在启动的时候只能指定appium server的端口么,不能知道wda的端口?

11362
Tank007 · #50 · 2017年1月12日

#49楼 @tongshanshanshan 跟你启动macaca差不多,都是一条命令,appium -a ip -p port,这个port就是启动的appium的端口,目前还没看到过指定wda的端口

11362
Tank007 · #51 · 2017年1月12日

#49楼 @tongshanshanshan wda启动成功后的日志

2017-01-12 16:49:00.609 XCTRunner[10928:159660] Running tests...
2017-01-12 16:49:02.465 XCTRunner[10928:159660] Continuing to run tests in the background with task ID 1
Test Suite 'Selected tests' started at 2017-01-12 16:49:03.066
Test Suite 'WebDriverAgentRunner.xctest' started at 2017-01-12 16:49:03.067
Test Suite 'UITestingUITests' started at 2017-01-12 16:49:03.068
Test Case '-[UITestingUITests testRunner]' started.
    t =     0.00s     Start Test at 2017-01-12 16:49:03.069
    t =     0.00s     Set Up
2017-01-12 16:49:03.077 XCTRunner[10928:159660] Built at Jan  6 2017 09:14:36
2017-01-12 16:49:03.136 XCTRunner[10928:159660] ServerURLHere->http://10.180.185.159:8100<-ServerURLHere
5338
tongshanshanshan · #52 · 2017年1月12日

#51楼 @Tank007 所以他默认就是8100,同时起2个还是8100?

11362
Tank007 · #53 · 2017年1月12日

#52楼 @tongshanshanshan 目前从日志来看,是这样的

2562
carl · #54 · 2017年1月12日

这是一台iOS和一台Android同时测试,还是支持多台iOS同时测试啊?

5338
tongshanshanshan · #55 · 2017年1月13日

#54楼 @carl 都支持

2562
carl · #56 · 2017年1月13日

#55楼 @tongshanshanshan
请问是不是也支持一台mac同时连接多台iOS啊?据说appium不支持一台mac连接多台iOS设备测试
感谢

5338
tongshanshanshan · #57 · 2017年1月13日

#56楼 @carl 支持的。appium我没有试过,你可以跟 @Tank007 他讨论一下,他在研究呢。

12574
AllocAndInit · #58 · 2017年1月13日

@tongshanshanshan 请问楼主 ,你有试过在 多台 iOS真机设备上并发执行测试用例过吗?是否了解 Macaca 有一个最大的并发数?我自己现在在 两台iOS真机设备上并行测试没什么问题,但是一旦添加一台设备以后,也就是 3 台设备同时测试的时候,总会出现一些莫名其妙的问题,而且也总是 有一台设备无法正常启动测试用例,而且还会 一直报 xctest client proxy error with: Error: socket hang up 这样的错误 ,请问楼主有碰到过这样的问题吗?

96
tfzhou · #59 · 2017年1月17日

#28楼 @tongshanshanshan 楼主的文章很好,就是对于小白来说很多看不懂,可以教教我怎么快速上手进行真机测试吗?非常想学,可是没头绪,谢谢楼主。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册