用java构建企业级自动化框架(第四篇-构建框架分布式并发执行测试用例功能1)

来源:互联网 发布:40部网络禁书免费阅读 编辑:程序博客网 时间:2024/05/17 03:43

   这个是我后来写的一本书,http://www.ituring.com.cn/minibook/10775。这个是我后来找到的自动化完美解决方案。

 对于一个自动化框架来说,它可以分为上下两层架构,上层关注用户如何使用,脚本语言是什么形式,报告以一种什么方式展出。底层关注的是大量测试用例如何并发式执行,多处理器如何配置等这些用户看不到的东西。这一章我们说下如何构建框架的底层,构建分布式并发执行测试脚本功能。

  今天这一节我们先给出第一个解决方案。rmi+jms处理方式。

首先说下rmi。它是搭建java分布式的基础,在java1.1中已经存在这个类,他是分布式及后来的webservice的基础。corejava7和8在第二卷高级篇中对它都有介绍,感兴趣的朋友可以先对着core java教程做下demo再看下面解决方案。当然最好用两台机子,没有两台机子的话你可以装个虚拟机。


假设我们有一个主机,几个分机,那我们来搭建一下的它的环境。

首先我们搭建一个RMI的接口,无论是客户端或者是服务器端都要继承这个接口,这个接口继承Rmote接口。

public interface BertRmiInterface extends Remote


写出它要实现的方法

    /**
     * The core method that executing test.

    public List<BertResult> execute(String area, int maxThreads, String outputDir, String logLevel, String qaEnv, BertCrampon ceng) throws RemoteException; // run test


    public String getBertRoot()throws RemoteException;
    /**
     * Terminate this testing process(jvm).
     */
    public void exit() throws RemoteException; //self terminating
    /**
     * to start Selenium RC
     * @return
     * @throws java.rmi.RemoteException
     */
    public int startRemoteControl() throws RemoteException;
    /**
     * to stop Selenium RC
     * @param port
     * @throws java.rmi.RemoteException
     */
    public void stopRemoteControl(int port) throws RemoteException;


    /**
     * upload dir to a shared folder
     * @param outputDir
     * @param remoteDir
     * @throws java.rmi.RemoteException
     */
    public void uploadOutput(String outputDir, String remoteDir) throws RemoteException;


    /**
     * call cmd in remote machine
     * @param cmd
     * @throws java.rmi.RemoteException
     */
    public int execCMD(String cmd) throws RemoteException;
    
    //may needed in future
    //public int getInvokingCount() throws RemoteException;  // how busy am I 
    //public void resetEnv()throws RemoteException;    // for reuse
}


接下来我们搭建服务器环境,它实现了接口的所有方法。注意里面的main()方法,它负责创建定位rmi主服务器对象。并绑定端口地址供客户端访问。

public class BertRmiServer extends UnicastRemoteObject implements BertRmiInterface{

  //这里设置线程的结束。 

public void exit() throws RemoteException{
 System.out.println("###RmiServer-["+boundName+"] - Terminiting server process****");
        Thread t = new Thread() {
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                }
                System.exit(0);
            }
        };
        t.start();
    }
    
    /**
     * This "remote" method to execute real testing, main steps:
     * 1,把管理好的测试列表加载进去,2.为每个测试用例开启一个线程。3,创建报告。
     * 
     * @return The result of this testing.
     * @param qaEnv QA environment
     * @param logLevel log level
     * @param area Business area to test
     * @param maxThreads Maximum concurrent BERT testing threads allowed
     * @param outputDir Output directory, for log & report
     * @throws java.rmi.RemoteException Required by RMI
     */
    public List<BertResult> execute(String area, int maxThreads, String outputDir, String logLevel, String qaEnv, BertCrampon ceng) throws RemoteException {


        System.out.println("###RmiServer-[" + area + "] -  begins to run with maxThreads " + maxThreads + " ****");
        this.logLevel = logLevel;
        Date start = new Date();
        Engine engine = new Engine();
        engine.startBertEngine(area, maxThreads, outputDir, logLevel, qaEnv,ceng);
        Date end = new Date();


        List<BertResult> retList = null;


        retList = new ArrayList<BertResult>();
        AreaReport rpt = new AreaReport(new ArrayList(BertParser.testcases.values()), outputDir + "reports/", area, start, end);
        rpt.startAreaReport();
        retList.add(rpt.getResult());
        
        return retList;
    }



    /**
     * To parse the CMD line arguments.
     */
    private void parseCmdlineArgs(String[] args){
        if( args.length < 1 ){
            System.out.println("###BertRmiServer- Error, at least 1 param required(RMI bound name), exit now...");
            System.exit(1);
        }
        boundName = args[0];
    }
    
    /**
     * The main method of the RMI server application, main flow:
     * 1.create an instance of BertRmiServer,
     * 2.bind it to a certain name and automatically begin RMI listening
     * @param args CMD line arguments, at least one argument required - the RMI binding name
     */
    public static void main(String[] args) {
        
        try {
            BertRmiServer serverobj = new BertRmiServer();
            serverobj.parseCmdlineArgs(args);


            BertController.enableJMSMessaging();


            Context c = new InitialContext();
            c.bind("rmi:"+ boundName, serverobj);
            System.out.println("###BertRmiServer-["+boundName+"] - begin listening****");


            
        } catch (RemoteException ex) {
            ex.printStackTrace();
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
        
    }


     /**
     * start Selenium RC
     * @return
     * @throws java.rmi.RemoteException
     */
    public int startRemoteControl() throws RemoteException {


        JstSeleniumRC jstRC= new JstSeleniumRC();
        jstRC.setIsEmbededRC(false);


        try {
            jstRC.start();
        } catch (ProcessingErrorException ex) {
            return -500;
        }


        this.jstServers.put(jstRC.getPortNumber()+"",jstRC);
        return jstRC.getPortNumber();
    }
    
    /**
     * stop Selenium RC
     */
    public void stopRemoteControl(int port) throws RemoteException {
        JstSeleniumRC jstRC = this.jstServers.get(port + "");
        if (jstRC != null) {
            jstRC.stop();
            this.jstServers.remove(port + "");
        }
    }


    /**
     * upload dir to a shared folder
     * @param outputDir
     * @param remoteDir
     * @throws java.rmi.RemoteException
     */
    public void uploadOutput(String outputDirBase, String remoteDir) throws RemoteException {
        String[] cmd = null;


        try {
            if (OS_NAME.contains("Windows")) {
                String outputDirFull = BertBase.getBertRoot() + "var" + File.separator + "output" + File.separator + outputDirBase;
                cmd = new String[]{"xcopy", outputDirFull, remoteDir, "/EYI"};
                
            } else {
                cmd = new String[]{BertBase.getBertBinRoot() + "samba-upload.sh", outputDirBase};
            }


            Runtime.getRuntime().exec(cmd);
            
        } catch (Exception ex) {
            System.out.println(" !!! error when uploadOutput: " + ex);
            ex.printStackTrace();
        }


    }


     /**
     * call cmd in remote machines
     * @param cmd
     * @throws java.rmi.RemoteException
     */
    public int execCMD(String cmd) throws RemoteException {


        try {
            Runtime.getRuntime().exec(cmd);
            return 0;
        } catch (IOException ex) {
            System.out.println(" !!! error when execute command: " + cmd  + " => "+ ex);
            return -1;
        }
    }


    // get BERT_R0OT from rmi call dynamically
    public String getBertRoot() throws RemoteException {
        return BertBase.getBertRoot();
    }

}

这里面把接口的所有方法给实现了,客户端只要把接口的方法通过Naming.lookup查找到这个服务器。就可以把服务器里面对接口的实现方法传到客户端。客户端的就自然的获得的service传回的数据了。

因为运行case是多线程启动运行的。所以客户端绑定了线程。

public class RmiClientThread implements Runnable{

public void run(){

 String url = "//localhost/";

 try{            
            StreamGobbler sg = new StreamGobbler(serverp.getInputStream());
            sg.start();
            
            BertRmiInterface br = (BertRmiInterface) lookupRmi(url + this.area, 30*1000, 100);
            if (br==null){
           List<BertResult> results = br.execute(this.area, this.maxThreads, this.outputDir, this.logLevel, this.qaEnv, this.ceng); // call remote method
            }catch (Exception ex){

            this.clientThreadLogger.error(this.getClass().getName()+" - ", ex);
        } 

}

}

    public static Remote lookupRmi(String name, int timeout, int interval) {
        System.out.println(BertRmiClientThread.class.getName() + "     begin to lookup rmi: " + name);


        int loops = timeout/interval>0 ? timeout/interval:1; // at least once
        int i = 0;
        boolean success = false;
        Remote br = null;
        for(; i<loops && (!success); i++){
            try {
                Thread.sleep(interval);
                    br = Naming.lookup(name);
            } catch (Exception ex) {
                continue;  //look up again
            }
            success = true;
        }
        if (success){           
            System.out.println(RmiClientThread.class.getName() + "     RMI lookup OK : " + name);
        } else {
            System.out.println(RmiClientThread.class.getName() + "     RMI lookup failed : " + name);
        }
        return br;
    }


到这里的话我们主机和分机之间的分布式架构就架设好了,那执行的时候如何设定它们之间的通信呢,现在开始说下jms

首先,定义一个rmi接口

public interface MessageManagerInterface extends Remote {


    /** RMI bound name */
    static final String BOUND_NAME = "msgManager";
    /** Config key for msg manager host */
    static final String MSG_MANAGER_RMI_HOST = "MSG_MANAGER_RMI_HOST";
    /** Config key for msg manager port */
    static final String MSG_MANAGER_RMI_PORT = "MSG_MANAGER_RMI_PORT";


    /**
     * Remote method that clients can call to send messages to the message queue.
     *
     * @param msg Message
     * @throws RemoteException Required by RMI
     */
    void sendMessage(String msg) throws RemoteException;
}


制定消息发送者类实现这个接口。

public class BertMessageManager extends UnicastRemoteObject implements MessageManagerInterface {

    /** Message queue sender */
    private QueueSender queueSender = null;
    /** Internal queue to act as a buffer in case the JMS message queue fails. */
    private ArrayBlockingQueue<String> internalQueue = new ArrayBlockingQueue<String>(QUEUE_MAX_SIZE);
    /** Internal queue maximum size */
    private static final int QUEUE_MAX_SIZE = 500;


    public MessageManager(String[] args) throws RemoteException
    {



//读取配置文件MSG_MANAGER_RMI_PORT

        int port = Integer.parseInt(Base.getConfig(MSG_MANAGER_RMI_PORT));
        RmiClient.startRmiregistry(port, msgManagerLogger);


        initSenderThread();
    }

   private void initSenderThread()
    {
        Thread senderThread = new Thread(new Runnable()
        {
            public void run()
            {
         while(true)
         {
                    initMessageQueue();


                    String msg = null;


                    try
                    {
                        // take method will block until the queue has some entry
                        msg = internalQueue.take();


                        QueueSession queueSession = MessageUtil.getQueueSession();


                        TextMessage myTextMsg = queueSession.createTextMessage();
                        myTextMsg.setText(msg);
                        queueSender.send(myTextMsg);
                        
                        // Since the message was sent successfully,
                        // reset the failure email sent flag.
                     MessageUtil.resetFailureEmailSentFlag();
                    }
                    catch (InterruptedException e)
                    {
                        msgManagerLogger.error(this.getClass().getName() + " - ", e);
                    }
                    catch (JMSException jmse)
                    {
                        msgManagerLogger.error(this.getClass().getName() +
                                " - Error while sending message to the message queue", jmse);
                        // Send email to jbert about this.
                        BertMessageUtil.sendEmail(jmse);
                        queueSender = null;
                        // Put the msg back on the internal queue
                        addMessageToInternalQueue(msg);
                    }
         }
            }
        });


        senderThread.start();
    }


  private void initMessageQueue()
    {
        while(queueSender == null)
        {
            MessageUtil.initMessageQueue();
            queueSender = BertMessageUtil.initSender();


            // If queueSender is still null, sleep for 10 seconds before
            // attempting to connect again so that the this thread
            // doesn't take up too much of the CPU cycles
            if(queueSender == null)
            {
                try {
                    Thread.sleep(10000);
                } catch(InterruptedException e) {
                    msgManagerLogger.error(this.getClass().getName() + " - ", e);
                }
            }


        }


    }

    public void sendMessage(String msg) throws RemoteException
    {
            if(internalQueue.remainingCapacity() > 0)
     {
            internalQueue.add(msg);
     }
    }

    public static MessageManagerInterface lookupMessageManager()
    {
        String host = Base.getConfig(MSG_MANAGER_RMI_HOST);
        String port = Base.getConfig(MSG_MANAGER_RMI_PORT);
        return (MessageManagerInterface) RmiClientThread.lookupRmi(

                "//" + host + ":" + port + "/" + BOUND_NAME, 100, 100);
    }


    /**
     * The main method of the RMI server application, main flow:
     * 1. Create an instance of BertMessageManager,
     * 2. Bind it to a certain name and automatically begin RMI listening
     * 
     * @param args Command line arguments
     * @throws NamingException
     * @throws RemoteException
     */

当下面程序启动,他就会发送信息。
    public static void main(String[] args) throws NamingException, RemoteException {
        MessageManager msgManager = new MessageManager(args);


        String host = Base.getConfig(MSG_MANAGER_RMI_HOST);
        String port = Base.getConfig(MSG_MANAGER_RMI_PORT);


        Context c = new InitialContext();
        c.bind("rmi://" + host + ":" + port + "/"+ BOUND_NAME, msgManager);
        msgManager.logInfo("["+ BOUND_NAME +"] - begin listening****");
    }

}

消息接受者接收发送者发到队列的消息。

public class MessageConsumer {


    /** Queue receiver */
    private QueueReceiver queueReceiver = null;
    /** Message consumer logger */
    private Logger msgConsumerLogger = null;


    /**
     * Constructor for BertMessageConsumer
     * 
     * @param args Command line arguments
     */
    public MessageConsumer(String[] args)
    {
        MessageUtil.initialize();



        receive();
    }


    /**
     * Initializes the message queue.
     */
    private void initMessageQueue()
    {
        while(queueReceiver == null)
        {
            MessageUtil.initMessageQueue();
            queueReceiver = MessageUtil.initReceiver();


            // If queueReceiver is still null, sleep for 10 seconds before
            // attempting to connect again so that the this thread
            // doesn't take up too much of the CPU cycles
            if(queueReceiver == null)
            {
                try {
                    Thread.sleep(10000);
                } catch(InterruptedException e) {
                    this.msgConsumerLogger.error(this.getClass().getName() + " - ", e);
                }
            }
        }
    }


    /**
     * Receives the messages from the message queue.
     */
    private void receive()
    {
        while(true)
        {
            try
            {
                initMessageQueue();


                Message msg = queueReceiver.receive();
                
                if (msg instanceof TextMessage) {
                    TextMessage txtMsg = (TextMessage) msg;
                    this.msgConsumerLogger.info(this.getClass().getName() + " - " + txtMsg.getText());
                }


                // Since the message was received successfully,
                // reset the failure email sent flag.
               MessageUtil.resetFailureEmailSentFlag();
            }
            catch(JMSException e)
            {
                this.msgConsumerLogger.error(this.getClass().getName() + " - ", e);
                tMessageUtil.sendEmail(e);
                queueReceiver = null;
            }
        }


    }


    /**
     * Main method. Calls the BertMessageManager constructor.
     *
     * @param args Command line arguments.
     *
     */
    public static void main(String[] args)
    { 

new MessageConsumer(args);

}

}

最后这个工具类实现jms的接口函数供前面发送者接受者调用。

public class MessageUtil
{
    /** JNDI Context */
    private static Context                      ctx = null;
    /** Message queue connection factory */
    private static QueueConnectionFactory qcf = null;
    /** Message queue connection */
    private static QueueConnection queueConnection = null;
    /** Message queue session */
    private static QueueSession        queueSession = null;
    /** Message queue */
    private static Queue queue = null;
    /** Message queue sender */
    private static QueueSender                  queueSender = null;
    /** Message queue sender */
    private static QueueReceiver                queueReceiver = null;
    /** Logger */
    private static Logger                       logger = null;
    /** Flag to indicate whether email has been sent about message queue failure */
    private static boolean failureEmailSent = false;
    /** Output directory */
    private static String outputDir = null;


    /** Message queue connection factory lookup name for jbert */
    public static final String CF_LOOKUP_NAME = "cn=ConnectionFactory";
    /** Message queue lookup name for jbert */
    public static final String QUEUE_LOOKUP_NAME = "cn=automationtool";



    /**
     * Initializes message queue.
     */
    protected static void initMessageQueue()
    {
        // Reset the static variables
        ctx = null;
        qcf = null;
        queueConnection = null;
        queue = null;
        queueSession = null;
        queueSender = null;
        queueReceiver = null;

 config.load(new FileInputStream(CONF_ROOT + "appproperties.properties"));


        if(ctx != null) { MessageUtil.initConnectionFactory(); }


        if(qcf != null) { MessageUtil.initQueue(); }
        
        if(queue != null) { MessageUtil.initConnection(); }


        if(queueConnection != null) { MessageUtil.initSession(); }
    }
    
    /**
     * Initializes the context
     */
    private static void initContext()
    {
        Hashtable env = new Hashtable();
        String ldapUrl = Base.getConfig("JMS_LDAP_URL");
        // Store the environment variables that tell JNDI which initial context
        // to use and where to find the provider.
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, ldapUrl);


        try {
            // Create the initial context.
            ctx = new InitialContext(env);
        } catch (NamingException ne)  {
            sendEmail(ne);
        }
    }


    /**
     * Initializes the connection factory
     */
    private static void initConnectionFactory()
    {
        try {
            // Lookup my connection factory from the admin object store.
            // The name used here here must match the lookup name
            // used when the admin object was stored.
            logger.info(MessageUtil.class.getName() + " - Looking up Queue Connection Factory object with lookup name: "
                        + CF_LOOKUP_NAME);
            qcf = (javax.jms.QueueConnectionFactory) ctx.lookup(CF_LOOKUP_NAME);
            logger.info(BertMessageUtil.class.getName() + " - Queue Connection Factory object found.");
        } catch (NamingException ne)  {

            sendEmail(ne);
        }


    }


    /**
     * Initializes the queue
     */
    private static void initQueue()
    {
        try {
            // Lookup the queue from the admin object store.
            // The name used here must match the lookup name used when
            // the admin object was stored.
            logger.info(BertMessageUtil.class.getName() + " - Looking up Queue object with lookup name: "
                        + JBERTQUEUE_LOOKUP_NAME);
            queue = (javax.jms.Queue)ctx.lookup(JBERTQUEUE_LOOKUP_NAME);
            logger.info(BertMessageUtil.class.getName() + " - Queue object found.");
        } catch (NamingException ne)  {
            sendEmail(ne);
        }
    }


    /**
     * Initializes the connection
     */
    private static void initConnection()
    {
        try {

            queueConnection = qcf.createQueueConnection();
        } catch (JMSException e)  {

            sendEmail(e);
        }
    }


    /**
     * Initializes the queue session
     *
     * @return Queue session
     */
    private static void initSession()
    {
        try {
            queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
        } catch (JMSException e)  {
            sendEmail(e);
        }
    }


    /**
     * Initializes the sender
     *
     * @return QueueSender
     */
    protected static QueueSender initSender()
    {
        if(queueSession != null)
        {
            try {


                // Create the queueSender
                queueSender = queueSession.createSender(queue);


                // Tell the provider to start sending messages.
                queueConnection.start();


            } catch (JMSException e)  {
                logger.error(MessageUtil.class.getName() + " - JMS Exception: " , e);
                sendEmail(e);
            }
        }
        return queueSender;
    }


原创粉丝点击