当前位置:网站首页>接口自动化框架脚手架-利用反射机制实现接口统一发起端
接口自动化框架脚手架-利用反射机制实现接口统一发起端
2022-06-28 09:41:00 【软件质量保障】
持续坚持原创输出,点击蓝字关注我吧
作者:软件质量保障
知乎:https://www.zhihu.com/people/iloverain1024
接口自动化框架的归途是平台化、页面可操作化,而非少数懂代码的测试同学使用的。因此,前端就需要一个统一的接口调用服务,将需要调用的服务转发给后端,真正触发用户发起的服务。
一、概述
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。
特别是在设计或运行中添加新类时,能够快速地应用开发工具动态地查询新添加类的能力。反射机制可以用来:
在运行时分析类的能力。
在运行时查看对象,例如,编写一个toString方法供所有类使用。
实现通用的数组操作代码。

使用 Java 反射,只需要引入JDK内置的java.lang.reflect包中的类,不用引入额外的Maven配置。

二、开胃菜
先看一个非常简单的示例,该示例在运行时检查一个简单Java对象的属性。先创建一个简单的Person类,它只有name和age属性,没有方法。Person 类如下:
public class Person {private String name;private int age;}
下面的代码使用 Java 反射来获取这个类的所有属性。先实例化一个Person对象并使用 Object 作为声明类型:
private static List<String> getFieldNames(Field[] fields) {List<String> fieldNames = new ArrayList<>();for (Field field : fields)fieldNames.add(field.getName());return fieldNames;}@Testpublic void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {Object person = new Person();Field[] fields = person.getClass().getDeclaredFields();List<String> actualFieldNames = getFieldNames(fields);assertTrue(Arrays.asList("name", "age").containsAll(actualFieldNames));}
通过这个测试case,我们能够从person对象中获取一个属性对象数组,即使对对象的引用是该对象的父类型(Object)。
三、探寻类
在本节中,我们将探讨 Java 反射 API 的基础用法。通过测试case熟悉如何利用反射API获取对象的信息,例如对象的类名、修饰符、属性、方法、实现的接口等。
3.1 项目准备
以动物为对象,先定义动物吃的行为接口,该接口定义了Animal对象的进食行为。然后让我们创建一个实现Eating接口的抽象Animal类。。
Eating接口:
public interface Eating {String eats();}
下面是实现Eating接口的抽象类Animal实现:
public abstract class Animal implements Eating {public static String CATEGORY = "domestic";private String name;protected abstract String getSound();// constructor, standard getters and setters omitted}
创建另一个名为Locomotion的接口来描述动物如何移动:
public interface Locomotion {String getLocomotion();}
创建一个名为Goat的抽象类,它继承Animal并实现Locomotion接口。由于超类实现了Eating,因此Goat也必须实现该接口的方法:
public class Goat extends Animal implements Locomotion {@Overrideprotected String getSound() {return "bleat";}@Overridepublic String getLocomotion() {return "walks";}@Overridepublic String eats() {return "grass";}}
OK,万事俱备,下面使用 Java 反射来获取出现在上述类和接口中的 Java 对象的各种信息。
3.2. 类名
让我们首先从Class获取对象的名称:
@Testpublic void givenObject_whenGetsClassName_thenCorrect() {Object goat = new Goat("goat");Class<?> clazz = goat.getClass();assertEquals("Goat", clazz.getSimpleName());assertEquals("com.baeldung.reflection.Goat", clazz.getName());assertEquals("com.baeldung.reflection.Goat", clazz.getCanonicalName());}
Class的getSimpleName方法返回对象的基本名称,而另外两个方法返回其完整类名。
如果我们只知道它的完整类名,看看能否创建一个Goat类的对象:
@Testpublic void givenClassName_whenCreatesObject_thenCorrect(){Class<?> clazz = Class.forName("com.baeldung.reflection.Goat");assertEquals("Goat", clazz.getSimpleName());assertEquals("com.baeldung.reflection.Goat", clazz.getName());assertEquals("com.baeldung.reflection.Goat", clazz.getCanonicalName());}
我们给静态方法forName的参数应该包含类包信息,否则,我们会得到一个ClassNotFoundException。
3.3. 类修饰符
我们可以调用getModifiers方法来获取类的修饰符,该方法返回一个Integer。
java.lang.reflect.Modifier类提供静态方法来分析返回的Integer是否存在特定修饰符。
让我们看下前文定义的一些类的修饰符:
@Testpublic void givenClass_whenRecognisesModifiers_thenCorrect() {try {Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");int goatMods = goatClass.getModifiers();int animalMods = animalClass.getModifiers();assertTrue(Modifier.isPublic(goatMods));assertTrue(Modifier.isAbstract(animalMods));assertTrue(Modifier.isPublic(animalMods));} catch (ClassNotFoundException e){e.printStackTrace();}}
当然我们通过这种方法也能够获取项目中引入 jar 的类修饰符。
3.4. Package
我们还能够获取有关任何类或对象的包的信息,通过调用类对象的getPackage方法返回。
@Testpublic void givenClass_whenGetsPackageInfo_thenCorrect() {Goat goat = new Goat("goat");Class<?> goatClass = goat.getClass();Package pkg = goatClass.getPackage();assertEquals("com.baeldung.reflection", pkg.getName());}
3.3. 超类
在许多情况下,尤其是在使用库类或 Java 的内置类时,我们可能事先不知道我们正在使用的对象的超类。
但是我们可以通过使用Java反射来获取任何 Java 类的超类,下面代码来获取Goat的超类。
@Testpublic void givenClass_whenGetsSuperClass_thenCorrect() {Goat goat = new Goat("goat");String str = "any string";Class<?> goatClass = goat.getClass();Class<?> goatSuperClass = goatClass.getSuperclass();assertEquals("Animal", goatSuperClass.getSimpleName());assertEquals("Object", str.getClass().getSuperclass().getSimpleName());}
3.6. 实现的接口
使用 Java 反射,我们还能够获取给定类实现的接口列表。让我们获取Goat类和Animal抽象类实现的接口的类类型:
@Testpublic void givenClass_whenGetsImplementedInterfaces_thenCorrect(){Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");Class<?>[] goatInterfaces = goatClass.getInterfaces();Class<?>[] animalInterfaces = animalClass.getInterfaces();assertEquals(1, goatInterfaces.length);assertEquals(1, animalInterfaces.length);assertEquals("Locomotion", goatInterfaces[0].getSimpleName());assertEquals("Eating", animalInterfaces[0].getSimpleName());}
从断言中注意到,每个类只实现一个接口。检查这些接口的名称,我们发现Goat实现了Locomotion和Animal实现了Eating。
我们可以发现Goat是抽象类Animal的子类,也实现了接口方法eats(),Goat其实也实现了Eating接口。
但是需要注意的是,只有类显式声明为使用implements关键字实现的那些接口才会出现在返回的数组中。
因此,即使一个类实现了接口方法(继承的父类实现了该接口方法),但其没有直接使用implements关键字声明该接口,该接口也不会出现在返回的接口数组中。
3.7. 构造函数、方法和属性
使用 Java 反射,我们还能够获取任何对象类的构造函数以及方法和属性。
private static List<String> getMethodNames(Method[] methods) {List<String> methodNames = new ArrayList<>();for (Method method : methods)methodNames.add(method.getName());return methodNames;}
让我们看看如何获取Goat类的构造函数:
@Testpublic void givenClass_whenGetsConstructor_thenCorrect(){Class<?> goatClass = Class.forName("com.baeldung.reflection.Goat");Constructor<?>[] constructors = goatClass.getConstructors();assertEquals(1, constructors.length);assertEquals("com.baeldung.reflection.Goat", constructors[0].getName());}
我们还可以获取Animal类的属性:
@Testpublic void givenClass_whenGetsFields_thenCorrect(){Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");Field[] fields = animalClass.getDeclaredFields();List<String> actualFields = getFieldNames(fields);assertEquals(2, actualFields.size());assertTrue(actualFields.containsAll(Arrays.asList("name", "CATEGORY")));}
我们可以类似地获取Animal类的方法:
@Testpublic void givenClass_whenGetsMethods_thenCorrect(){Class<?> animalClass = Class.forName("com.baeldung.reflection.Animal");Method[] methods = animalClass.getDeclaredMethods();List<String> actualMethods = getMethodNames(methods);assertEquals(4, actualMethods.size());assertTrue(actualMethods.containsAll(Arrays.asList("getName","setName", "getSound")));}
四、在接口自动化平台中的应用
接口自动化框架的归途是平台化、页面可操作化,而非少数懂代码的测试同学使用的。因此,前端就需要一个统一的接口调用服务,将需要调用的服务转发给后端,真正触发用户发起的服务。可以参考下面的流程图。

例如美团接口自动化测试平台Lego,用户将服务名、测试method、request等内容作为输入,发起后会统一调后端的服务发起接口。

再比如淘宝开放平台,也是同样的实现方式。
https://open.taobao.com/new/apitesttool?spm=a219a.15212433.0.0.1c41669aDvRvXZ&apiName=taobao.appstore.subscribe.get

因此,接口测试平台的实现,反射机制是非常重要的。(当然有些自动化测试平台可能不是Java语言开发的,但是同样会有反射的影子)
下面我就以简单的例子演示下,项目结构如下:

1.开发两个service(API)
public class CreateService {public boolean create(String name){if (null !=name){System.out.println("-----create success----");return true;}System.out.println("-----create failure----");return false;}}public class QueryService {public boolean query(String name){if (null !=name){System.out.println("-----query success----");return true;}System.out.println("-----query failure----");return false;}}
利用反射机制实现统一接口调出服务,其入参为类名、被调方法、方法的request
public class Invoke {public void invokeProxy(String serviceName, String methodName, String request) {try {Class<?> serviceClass = Class.forName(serviceName);Object service = serviceClass.getDeclaredConstructor().newInstance();Method method = serviceClass.getMethod(methodName, String.class);method.invoke(service, request);} catch (ClassNotFoundException e){e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}}
测试Invoke.invokeProxy接口
@Testpublic void testInvokeProxy(){String serviceName = "cn.qa.reflect.demo.service.CreateService";String request = "testName";String methodname = "create";invokeProxy(serviceName, methodName, request);}
测试结果
-----create success----ok,这就实现了一个简单的统一接口调出服务。
喜欢的话,就点个赞和在看再走吧
- END -
下方扫码关注 软件质量保障,与质量君一起学习成长、共同进步,做一个职场最贵Tester!
往期推荐
边栏推荐
- 【OpenCV 例程200篇】213. 绘制圆形
- Interpretation of new products: realm launched GT neo2 Dragon Ball customized version
- 01 distributed system overview
- Installing redis under Linux and windows (ultra detailed graphic tutorial)
- Check whether the table contains rows SQL Server 2005 - check whether a table contains rows or not SQL Server 2005
- Dbeaver连接人大金仓KingbaseES V8(超详细图文教程)
- 【云驻共创】DWS告警服务DMS详细介绍和集群连接方式简介
- 云服务器MYSQL查询速度慢
- 结巴分词器_分词器原理
- Sword finger offer | linked list transpose
猜你喜欢

Installing redis under Linux and windows (ultra detailed graphic tutorial)

再见!IE浏览器,这条路由Edge替IE继续走下去

组合模式(Composite Pattern)

PyGame game: "Changsha version" millionaire started, dare you ask? (multiple game source codes attached)

为什么 Istio 要使用 SPIRE 做身份认证?

Bron filter Course Research Report

Thread lifecycle

PMP考试重点总结五——执行过程组

虛擬機14安裝win7(圖教程)

Unity AssetBundle asset packaging and asset loading
随机推荐
Unity AssetBundle资源打包与资源加载
PMP Exam key summary IX - closing
创建多线程的方法---1创建Thread类的子类及多线程原理
函数的分文件编写
Key summary IV of PMP examination - planning process group (2)
Full link service tracking implementation scheme
This article explains in detail the difficult problems and solutions faced by 3D cameras
Matplotlib attribute and annotation
Numpy array: join, flatten, and add dimensions
股票开户用中金证券经理发的开户二维码安全吗?知道的给说一下吧
Bridge mode
PMP Exam key summary VI - chart arrangement
Django数据库操作以及问题解决
Looking at jBPM from jbm3 to jbm5 and activiti
桥接模式(Bridge)
Dbeaver连接人大金仓KingbaseES V8(超详细图文教程)
Check whether the table contains rows SQL Server 2005 - check whether a table contains rows or not SQL Server 2005
Flip CEP skip policy aftermatchskipstrategy Skippastlastevent() matched no longer matches the Bikeng Guide
Ingersoll Rand panel maintenance IR Ingersoll Rand microcomputer controller maintenance xe-145m
On the influence of small program on the digitalization of media industry