基于 Appium 的移动测试框架,Appium 的 Wrapper 很关键。Talk is cheap, Java 版本代码如下:

package com.ctrip.cap.lanucher;

/**
 * A service wrapper for Appium Server
 * 
 * @author ltyao
 *
 */
public class AppiumServer {

    private static final Logger logger = LoggerFactory
            .getLogger(AppiumServer.class);
    private static final long START_TIMEOUT_MILLISECONDS = 30000;
    private static final HttpClient httpClient = HttpClients.createDefault();
    private static final String STATUS_PATH = "/wd/hub/status";
    private static final String PATH = "/wd/hub";
    private String ip = "localhost";

    // private volatile boolean started = false;

    private Process process;

    private int appiumPort = -1;
    private int bootstrapPort = -1;
    private int selendroidPort = -1;
    private int chromeDriverPort = -1;
    private int robotPort = -1;

    private String appiumLog;

    private AndroidDevice device;

    public AppiumServer() {
    }

    public AppiumServer(AndroidDevice device) {
        this.device = device;
    }

    public void stopAppium() {
        try {
            WinProcess winp = new WinProcess(process);
            logger.warn("try to kill process {} Recursively", winp.getPid());
            winp.killRecursively();
        } catch (Exception e) {
            logger.warn("stopAppium", e);
        }
    }


    public void startAppium() {
        startAppium(START_TIMEOUT_MILLISECONDS);
    }

    public void startAppium(long milliseconds) {
        List<String> cmds = buildCmds();

        final ProcessBuilder pb = new ProcessBuilder(cmds)
                .redirectErrorStream(true);

        logger.debug("start appium with {}", cmds.toString());

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    process = pb.start();

                    int exitvalue = process.waitFor();

                    logger.warn("appium server exitvalue {}", exitvalue);
                } catch (Exception e) {
                    logger.warn("startAppium", e);
                }

            }
        }).start();

        long start = System.currentTimeMillis();
        boolean state = isRunning();
        while (!state) {
            long end = System.currentTimeMillis();
            if (end - start > milliseconds) {
                this.stopAppium();
                throw new AppiumTimeoutException("Appium can't be lanuched in "
                        + milliseconds + " seconds");
            }
            state = isRunning();
        }

        logger.warn("started appium server {}", this);
    }

    public void restartAppium() {
        this.stopAppium();
        this.startAppium();
    }

    public boolean isRunning() {
        try {
            URI uri = new URIBuilder().setScheme("http").setHost(ip)
                    .setPort(appiumPort).setPath(STATUS_PATH).build();

            HttpGet httpget;
            HttpResponse response;
            httpget = new HttpGet(uri);
            response = httpClient.execute(httpget);
            HttpEntity entity = response.getEntity();
            String rs = EntityUtils.toString(entity);
            JsonElement json = new JsonParser().parse(rs);
            int status = json.getAsJsonObject().get("status").getAsInt();
            return status == 0;

        } catch (Exception e) {
            // logger.warn("isRunning", e);
            return false;
        }

    }

    public URL getURL() {
        URI uri;
        try {
            uri = new URIBuilder().setScheme("http").setHost(ip)
                    .setPort(appiumPort).setPath(PATH).build();
            return uri.toURL();
        } catch (URISyntaxException | MalformedURLException e) {
            throw new CapException("getURL", e);
        }

    }


    public int getPid() {
        try {
            WinProcess winp = new WinProcess(process);
            return winp.getPid();
        } catch (Exception e) {
            logger.warn("", e);
            return -1;
        }
    }

    /**
     * need to be checked
     * 
     * @return
     */
    private List<String> buildCmds() {

        appiumPort = AvailablePortFinder.getNextAvailable();
        chromeDriverPort = AvailablePortFinder.getNextAvailable();
        bootstrapPort = AvailablePortFinder.getNextAvailable();
        selendroidPort = AvailablePortFinder.getNextAvailable();

        List<String> cmds = new LinkedList<>();
        cmds.add("appium.cmd");
        cmds.add(String.format("--port=%d", appiumPort));
        cmds.add(String.format("--chromedriver-port=%d", chromeDriverPort));
        cmds.add(String.format("--selendroid-port=%d", selendroidPort));
        cmds.add(String.format("--bootstrap-port=%d", bootstrapPort));
        this.appiumLog = Environment.appiumLog(device.getSerialNumber());
        String qappiumLog = StringUtils.quoteArgument(this.appiumLog);
        cmds.add(String.format("--log=%s", qappiumLog));
        cmds.add("--log-timestamp");

        // switch (config.getDriverType()) {
        // case Selendroid:
        // selendroidPort = AvailablePortFinder.getNextAvailable();
        // cmds.add(String.format("--selendroid-port=%d", selendroidPort));
        //
        // break;
        // case ChromeDriver:
        // chromeDriverPort = AvailablePortFinder.getNextAvailable();
        // cmds.add(String.format("--chromedriver-port=%d", chromeDriverPort));
        //
        // default:
        // break;
        // }
        return cmds;
    }

    public int getAppiumPort() {
        return appiumPort;
    }

    public void setAppiumPort(int appiumPort) {
        this.appiumPort = appiumPort;
    }

    public int getBootstrapPort() {
        return bootstrapPort;
    }

    public void setBootstrapPort(int bootstrapPort) {
        this.bootstrapPort = bootstrapPort;
    }

    public int getSelendroidPort() {
        return selendroidPort;
    }

    public void setSelendroidPort(int selendroidPort) {
        this.selendroidPort = selendroidPort;
    }

    public int getChromeDriverPort() {
        return chromeDriverPort;
    }

    public void setChromeDriverPort(int chromeDriverPort) {
        this.chromeDriverPort = chromeDriverPort;
    }

    public int getRobotPort() {
        return robotPort;
    }

    public void setRobotPort(int robotPort) {
        this.robotPort = robotPort;
    }

    public String getAppiumLog() {
        return appiumLog;
    }

    public Device getDevice() {
        return device;
    }

    public void setDevice(AndroidDevice device) {
        this.device = device;
    }

    @Override
    public String toString() {
        return "AppiumServer [pid=" + getPid() + ",ip=" + ip + ", appiumPort="
                + appiumPort + ", bootstrapPort=" + bootstrapPort
                + ", selendroidPort=" + selendroidPort + ", chromeDriverPort="
                + chromeDriverPort + ", appiumLog=" + appiumLog + "]";
    }

}


↙↙↙阅读原文可查看相关链接,并与作者交流