zoukankan      html  css  js  c++  java
  • APP自动化框架-ATX原理解析及JAVA版客户端

    作为网易开源的ATX APP自动化测试框架,对比现有的macaca自动化框架/Appium自动化框架,最大的特别就是在于可远程进行自动化测试

    先给大家看一张我自己梳理的框架架构图

    框架巧妙点:

    1. 使用golang作为server端运行在Android手机上,免root运行

    2. AutomatorHttpService使用NanoHTTPD框架,也自己运行一个server,专门监听及处理过来的http jsonRpc请求

    public class AutomatorHttpServer extends NanoHTTPD {
    
        public AutomatorHttpServer(int port) {
            super(port);
        }
    
        private Map<String, JsonRpcServer> router = new HashMap<String, JsonRpcServer>();
    
        public void route(String uri, JsonRpcServer rpc) {
            router.put(uri, rpc);
        }
    
        @Override
        public Response serve(String uri, Method method,
                              Map<String, String> headers, Map<String, String> params,
                              Map<String, String> files) {
            Log.d(String.format("URI: %s, Method: %s, params, %s, files: %s", uri, method, params, files));
    
            if ("/stop".equals(uri)) {
                stop();
                return newFixedLengthResponse("Server stopped!!!");
            } else if ("/ping".equals(uri)) {
                return newFixedLengthResponse("pong");
            } else if ("/screenshot/0".equals(uri)) {
                float scale = 1.0f;
                if (params.containsKey("scale")) {
                    try {
                        scale = Float.parseFloat(params.get("scale"));
                    } catch (NumberFormatException e) {
                    }
                }
                int quality = 100;
                if (params.containsKey("quality")) {
                    try {
                        quality = Integer.parseInt(params.get("quality"));
                    } catch (NumberFormatException e) {
                    }
                }
                File f = new File(InstrumentationRegistry.getTargetContext().getFilesDir(), "screenshot.png");
                UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).takeScreenshot(f, scale, quality);
    
                try {
                    return newChunkedResponse(Response.Status.OK, "image/png", new FileInputStream(f));
                } catch (FileNotFoundException e) {
                    Log.e(e.getMessage());
                    return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
                }
            } else if (router.containsKey(uri)) {
                JsonRpcServer jsonRpcServer = router.get(uri);
                ByteArrayInputStream is = null;
                if (params.get("NanoHttpd.QUERY_STRING") != null)
                    is = new ByteArrayInputStream(params.get("NanoHttpd.QUERY_STRING").getBytes());
                else if (files.get("postData") != null)
                    is = new ByteArrayInputStream(files.get("postData").getBytes());
                else
                    return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Invalid http post data!");
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                try {
                    jsonRpcServer.handleRequest(is, os);
                    return newFixedLengthResponse(Response.Status.OK, "application/json", new ByteArrayInputStream(os.toByteArray()), os.size());
                } catch (IOException e) {
                    return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
                }
            } else
                return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found!!!");
        }
    
    }

    3. 使用jsonRpc反射反射形式对外提供 uiautomator方式

    package com.github.uiautomator.stub;
    
    import android.content.Context;
    import android.content.Intent;
    import android.os.RemoteException;
    import android.support.test.InstrumentationRegistry;
    import android.support.test.filters.LargeTest;
    import android.support.test.filters.SdkSuppress;
    import android.support.test.runner.AndroidJUnit4;
    import android.support.test.uiautomator.By;
    import android.support.test.uiautomator.Configurator;
    import android.support.test.uiautomator.UiDevice;
    import android.support.test.uiautomator.Until;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.googlecode.jsonrpc4j.JsonRpcServer;
    
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    /**
     * Use JUnit test to start the uiautomator jsonrpc server.
     *
     * @author xiaocong@gmail.com
     */
    @RunWith(AndroidJUnit4.class)
    @SdkSuppress(minSdkVersion = 18)
    public class Stub {
        private final String TAG = "UIAUTOMATOR";
        private static final int LAUNCH_TIMEOUT = 5000;
    
    
        int PORT = 9008;
        AutomatorHttpServer server = new AutomatorHttpServer(PORT);
    
        @Before
        public void setUp() throws Exception {
            launchService();
            //这是关键核心代码,把AutomatorService使用jsonRpcServer进行反射处理
            server.route("/jsonrpc/0", new JsonRpcServer(new ObjectMapper(), new AutomatorServiceImpl(), AutomatorService.class));
            server.start();
        }
    
        private void launchPackage(String packageName) {
            Log.i(TAG, "Launch " + packageName);
            UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
            Context context = InstrumentationRegistry.getContext();
            final Intent intent = context.getPackageManager()
                    .getLaunchIntentForPackage(packageName);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            context.startActivity(intent);
    
            device.wait(Until.hasObject(By.pkg(packageName).depth(0)), LAUNCH_TIMEOUT);
            device.pressHome();
        }
    
        private void launchService() throws RemoteException {
            UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
            Context context = InstrumentationRegistry.getContext();
            device.wakeUp();
    
            // Wait for launcher
            String launcherPackage = device.getLauncherPackageName();
            Boolean ready = device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);
            if (!ready) {
                Log.i(TAG, "Wait for launcher timeout");
                return;
            }
    
            Log.d("Launch service");
            context.startService(new Intent("com.github.uiautomator.ACTION_START"));
    
            // Reset Configurator Wait Timeout
            Configurator configurator = Configurator.getInstance();
            configurator.setWaitForSelectorTimeout(0L);
    
            // BUG(uiautomator): setWaitForIdleTimeout is useless
            // Refs: https://www.ydkf.me/archives/22
        }
    
        @After
        public void tearDown() {
            server.stop();
            Context context = InstrumentationRegistry.getContext();
            context.startService(new Intent("com.github.uiautomator.ACTION_STOP"));
        }
    
        @Test
        @LargeTest
        public void testUIAutomatorStub() throws InterruptedException {
            while (server.isAlive()) {
                Thread.sleep(100);
            }
        }
    }

    4. AutomatorServiceImpl把原生UiAutomation加了一定处理,重写了一遍,只要确保入参数保持一致

    @Override
        public boolean click(int x, int y) {
            return device.click(x, y);
        }
        @Override
        public boolean drag(int startX, int startY, int endX, int endY, int steps) throws NotImplementedException {
            return device.drag(startX, startY, endX, endY, steps);
        }

    从整体而言,代码简洁、可读性、代码解耦,在ATX上提现较为明显

    附上我这边写的java版ATX客户端,原框架只提供了python版

    https://github.com/tigerge000/atxuiautomatorclient

  • 相关阅读:
    request请求与响用
    flask源码分析
    falsk使用
    偏导函数
    flaskwsgiref
    请求和响应的周期执行顺序与异常和过滤器和模板语法
    C#读取EXCEL数据
    ecshop
    Log4Net记录日志(mvc)
    select和checkbox回绑
  • 原文地址:https://www.cnblogs.com/feihufeihu/p/10641514.html
Copyright © 2011-2022 走看看