51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2654|回复: 3
打印 上一主题 下一主题

[转贴] 使用 java 动态加载机制模拟脚本语言的效果

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2017-6-22 10:28:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
缘由有时我们需要测试某个方法,需要频繁的修改这个方法,但又不想重新去run整个程序,怎么做呢?
一个正常的appium测试用例
  1. package com.dynamicclassloader;

  2. import java.io.File;
  3. import java.net.URL;
  4. import org.junit.After;
  5. import org.junit.Before;
  6. import org.junit.Test;
  7. import org.openqa.selenium.WebElement;
  8. import org.openqa.selenium.remote.DesiredCapabilities;
  9. import com.dynamicclassloader.DynamicEngine;
  10. import com.my.Utils;

  11. import io.appium.java_client.android.AndroidDriver;
  12. import io.appium.java_client.remote.AndroidMobileCapabilityType;
  13. import io.appium.java_client.remote.AutomationName;
  14. import io.appium.java_client.remote.MobileCapabilityType;

  15. public class CommonTest {

  16.     private AndroidDriver<?> driver;
  17.     public DesiredCapabilities capabilities = new DesiredCapabilities();
  18.     public String location = "C:/Users/test.apk";
  19.     public String pkgName = "";
  20.     public String activityName = "";

  21.     public String getSN() {
  22.         String sn = Utils.runCMD(new String[]{"adb", "devices"}).split("\n")[1].split("\t")[0];

  23.         return sn;
  24.     }

  25.     public void updatePkgActivity() {
  26.         String pkgInfo = Utils.runCMD(new String[]{"aapt", "dump", "badging", location});

  27.         String pkgStart = "package: name='";
  28.         pkgName = pkgInfo.substring(pkgInfo.indexOf(pkgStart) + pkgStart.length(), pkgInfo.indexOf("' version"));

  29.         String activityStart = "launchable-activity: name='";
  30.         activityName = pkgInfo.substring(pkgInfo.indexOf(activityStart) + activityStart.length(), pkgInfo.indexOf("'  label=''"));

  31.     }

  32.     public void updateCapabilites() {
  33.         File app = new File(location);

  34.         capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, getSN());

  35.         updatePkgActivity();
  36.         capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, pkgName);
  37.         capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, activityName);

  38.         capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 300);

  39.         if (Utils.runCMD(new String[]{"adb", "shell", "pm", "path", pkgName}).equals("")) {
  40.             capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
  41.         }

  42.         capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
  43.     }

  44.     @Before
  45.     public void setUp() throws Exception {
  46.         updateCapabilites();
  47.         driver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);

  48.     }

  49.     @Test
  50.     public void stepTest() {
  51.         TouchAction ta = new TouchAction(driver);
  52.         ta.tap(306, 1852).perform();
  53.     }

  54.     @After
  55.     public void tearDown() throws Exception {
  56.         driver.quit();
  57.     }

  58. }
  59. 当我修改stepTest后,重新运行时,就会发现appium日志又刷刷地从头开始创建driver了,虽然driver已经创建完成了
  60. 动态加载方法
  61. Utils类中访问获取程序内部资源的方法
  62.     public String getStep(String fileName) {
  63.         String step = "";

  64.         InputStream is = getClass().getResourceAsStream(fileName);
  65.         BufferedReader br = new BufferedReader(new InputStreamReader(is));
  66.         String s = "";  
  67.         try {
  68.             while((s = br.readLine()) != null) {
  69.                 step += s;
  70. //              System.out.println(s);
  71.             }
  72.         } catch (IOException e) {
  73.             e.printStackTrace();
  74.         }

  75.         return step;
  76.     }
  77. 修改后的stepTest方法
  78. @Test
  79.     public void stepTest() {

  80.         while (true) {
  81.             String source;
  82.             DynamicEngine de = DynamicEngine.getInstance();

  83.             source = new Utils().getStep("AndroidStep.txt");

  84.             try {
  85.                 System.out.println("test");
  86.                 Class clazz =  de.javaCodeToObject("com.carl.AndroidStep", source);
  87.                 clazz.getMethod("runStep", AndroidDriver.class).invoke(clazz, driver);
  88.             } catch (Exception e) {
  89.                 e.printStackTrace();
  90.             }
  91.         }
  92.     }
  93. package com.dynamicclassloader;

  94. import java.io.File;
  95. import java.lang.reflect.Method;
  96. import java.net.URL;
  97. import java.net.URLClassLoader;
  98. import java.util.ArrayList;
  99. import java.util.List;

  100. import javax.tools.*;


  101. /**
  102. * 在Java中最好的方法是使用StandardJavaFileManager类。
  103. * 这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,
  104. * 而DiagnosticCollector类就是listener的实现。
  105. * 使用StandardJavaFileManager需要两步。
  106. * 首先建立一个DiagnosticCollector实例以及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。
  107. * 最后通过CompilationTask中的call方法编译源程序。
  108. */
  109. public class DynamicEngine {
  110.     //单例
  111.     private static DynamicEngine ourInstance = new DynamicEngine();

  112.     public static DynamicEngine getInstance() {
  113.         return ourInstance;
  114.     }
  115.     private URLClassLoader parentClassLoader;
  116.     private String classpath;
  117.     private DynamicEngine() {
  118.         //获取类加载器
  119.         this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();

  120.         //创建classpath
  121.         this.buildClassPath();
  122.     }

  123.     /**
  124.      * @MethodName    : 创建classpath
  125.      */
  126.     private void buildClassPath() {
  127.         this.classpath = null;
  128.         StringBuilder sb = new StringBuilder();
  129.         for (URL url : this.parentClassLoader.getURLs()) {
  130.             String p = url.getFile();
  131.             sb.append(p).append(File.pathSeparator);
  132.         }
  133.         this.classpath = sb.toString();
  134.         //System.out.println("classpath:" + this.classpath);
  135.     }

  136.     /**
  137.      * @MethodName    : 编译java代码到Object
  138.      * @Description    : TODO
  139.      * @param fullClassName   类名
  140.      * @param javaCode  类代码
  141.      * @return Object
  142.      * @throws Exception
  143.      */
  144.     public Class javaCodeToObject(String fullClassName, String javaCode) throws Exception {
  145.         long start = System.currentTimeMillis(); //记录开始编译时间
  146. //        Object instance = null;
  147.         //获取系统编译器
  148.         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  149.         // 建立DiagnosticCollector对象
  150.         DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

  151.         // 建立用于保存被编译文件名的对象
  152.         // 每个文件被保存在一个从JavaFileObject继承的类中
  153.         ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));

  154.         List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
  155.         jfiles.add(new StringSourceJavaObject(fullClassName, javaCode));

  156.         //使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合
  157.         List<String> options = new ArrayList<String>();
  158.         options.add("-encoding");
  159.         options.add("UTF-8");
  160.         options.add("-classpath");
  161.         options.add(this.classpath);

  162.         JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
  163.         // 编译源程序
  164.         boolean success = task.call();

  165.         Class clazz = null;
  166.         if (success) {
  167.             //如果编译成功,用类加载器加载该类
  168.             JavaClassObject jco = fileManager.getJavaClassObject();
  169.             DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
  170.             //String source = "package com.carl.test;public class Sourceee { public static void main(String[] args) {System.out.println(\"Hello World!\");} }";
  171.             clazz = dynamicClassLoader.loadClass(fullClassName, jco);
  172. //            instance = clazz.newInstance();

  173.             for (Method m: clazz.getDeclaredMethods()) {
  174.                 System.out.println("method name: " + m.getName());
  175. //                Class<?> classType = Class.forName("java.lang.String");
  176. //                m.invoke(clazz, Array.newInstance(classType, 0));
  177.             }
  178.         } else {
  179.             //如果想得到具体的编译错误,可以对Diagnostics进行扫描
  180.             String error = "";
  181.             for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
  182.                 error += compilePrint(diagnostic);
  183.             }
  184.         }
  185.         long end = System.currentTimeMillis();

  186.         return clazz;
  187. //        System.out.println("javaCodeToObject use:"+(end-start)+"ms");
  188. //        return instance;
  189.     }
复制代码

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2017-6-22 10:28:38 | 只看该作者
  1.   /**
  2.      * @MethodName    : compilePrint
  3.      * @Description    : 输出编译错误信息
  4.      * @param diagnostic
  5.      * @return
  6.      */
  7.     private String compilePrint(Diagnostic diagnostic) {
  8.         System.out.println("Code:" + diagnostic.getCode());
  9.         System.out.println("Kind:" + diagnostic.getKind());
  10.         System.out.println("Position:" + diagnostic.getPosition());
  11.         System.out.println("Start Position:" + diagnostic.getStartPosition());
  12.         System.out.println("End Position:" + diagnostic.getEndPosition());
  13.         System.out.println("Source:" + diagnostic.getSource());
  14.         System.out.println("Message:" + diagnostic.getMessage(null));
  15.         System.out.println("LineNumber:" + diagnostic.getLineNumber());
  16.         System.out.println("ColumnNumber:" + diagnostic.getColumnNumber());
  17.         StringBuffer res = new StringBuffer();
  18.         res.append("Code:[" + diagnostic.getCode() + "]\n");
  19.         res.append("Kind:[" + diagnostic.getKind() + "]\n");
  20.         res.append("Position:[" + diagnostic.getPosition() + "]\n");
  21.         res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
  22.         res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
  23.         res.append("Source:[" + diagnostic.getSource() + "]\n");
  24.         res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
  25.         res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
  26.         res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
  27.         return res.toString();
  28.     }
  29. }


  30. package com.dynamicclassloader;

  31. import javax.tools.SimpleJavaFileObject;
  32. import java.io.ByteArrayOutputStream;
  33. import java.io.IOException;
  34. import java.io.OutputStream;
  35. import java.net.URI;
  36. /**
  37. * 将输出流交给JavaCompiler,最后JavaCompiler将编译后的class文件写入输出流中
  38. */
  39. public class JavaClassObject extends SimpleJavaFileObject {

  40.     /**
  41.      * 定义一个输出流,用于装载JavaCompiler编译后的Class文件
  42.      */
  43.     protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();

  44.     /**
  45.      * 调用父类构造器
  46.      * @param name
  47.      * @param kind
  48.      */
  49.     public JavaClassObject(String name, Kind kind) {
  50.         super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
  51.     }

  52.     /**
  53.      * 获取输出流为byte[]数组
  54.      * @return
  55.      */
  56.     public byte[] getBytes() {
  57.         return bos.toByteArray();
  58.     }

  59.     /**
  60.      * 重写openOutputStream,将我们的输出流交给JavaCompiler,让它将编译好的Class装载进来
  61.      * @return
  62.      * @throws IOException
  63.      */
  64.     @Override
  65.     public OutputStream openOutputStream() throws IOException {
  66.         return bos;
  67.     }

  68.     /**
  69.      * 重写finalize方法,在对象被回收时关闭输出流
  70.      * @throws Throwable
  71.      */
  72.     @Override
  73.     protected void finalize() throws Throwable {
  74.         super.finalize();
  75.         bos.close();
  76.     }
  77. }

  78. package com.dynamicclassloader;

  79. import javax.tools.JavaFileObject;
  80. import javax.tools.SimpleJavaFileObject;
  81. import java.io.IOException;
  82. import java.net.URI;
  83. import java.net.URISyntaxException;

  84. public class StringSourceJavaObject extends SimpleJavaFileObject {
  85.     private String content = null;

  86.     public StringSourceJavaObject(String name, String content) throws URISyntaxException {
  87.         super(URI.create("string:///" + name.replace(".", "/") + JavaFileObject.Kind.SOURCE.extension), Kind.SOURCE);
  88.         this.content = content;
  89.     }

  90.     public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
  91.         return content;
  92.     }
  93. }

  94. package com.dynamicclassloader;

  95. import java.net.URL;
  96. import java.net.URLClassLoader;

  97. public class DynamicClassLoader extends URLClassLoader {
  98.     public DynamicClassLoader(ClassLoader parent) {
  99.         super(new URL[0], parent);
  100.     }

  101.     public Class findClassByClassName(String className) throws ClassNotFoundException {
  102.         return super.findClass(className);
  103.     }

  104.     public Class loadClass(String fullName, JavaClassObject jco) {
  105.         byte[] classData = jco.getBytes();
  106.         return super.defineClass(fullName, classData, 0, classData.length);
  107.     }
  108. }

  109. package com.dynamicclassloader;


  110. import java.io.IOException;
  111. import javax.tools.*;

  112. /**
  113. * 类文件管理器
  114. * 用于JavaCompiler将编译好后的class,保存到jclassObject中
  115. */
  116. public class ClassFileManager extends ForwardingJavaFileManager {

  117.     /**
  118.      * 保存编译后Class文件的对象
  119.      */
  120.     private JavaClassObject jclassObject;

  121.     /**
  122.      * 调用父类构造器
  123.      * @param standardManager
  124.      */
  125.     public ClassFileManager(StandardJavaFileManager standardManager) {
  126.         super(standardManager);
  127.     }

  128.     /**
  129.      * 将JavaFileObject对象的引用交给JavaCompiler,让它将编译好后的Class文件装载进来
  130.      * @param location
  131.      * @param className
  132.      * @param kind
  133.      * @param sibling
  134.      * @return
  135.      * @throws IOException
  136.      */
  137.     @Override
  138.     public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)
  139.             throws IOException {
  140.         if (jclassObject == null)
  141.             jclassObject = new JavaClassObject(className, kind);
  142.         return jclassObject;
  143.     }

  144.     public JavaClassObject getJavaClassObject() {
  145.         return jclassObject;
  146.     }
  147. }

  148. package com.carl;

  149. import io.appium.java_client.TouchAction;
  150. import io.appium.java_client.android.AndroidDriver;

  151. public class AndroidStep {

  152.     public static void runStep(AndroidDriver driver) {
  153.         TouchAction ta = new TouchAction(driver);
  154.         ta.tap(306, 1852).perform();
  155.     }

  156. }
复制代码

主要原理
动态加载一个java类,并反射调用
参考书籍
《Java深度历险》
回复 支持 反对

使用道具 举报

该用户从未签到

4#
 楼主| 发表于 2017-6-22 10:43:19 | 只看该作者
回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-14 14:34 , Processed in 0.060875 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表