Java SE基础知识详解第[19]期—单元测试、反射、注解、动态代理

简介: Java SE基础知识详解第[19]期—单元测试、反射、注解、动态代理

单元测试、反射、注解、动态代理

1.单元测试

1.1单元测试概述

单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。

Junit单元测试框架

JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。

此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。

JUnit优点

JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。

Junit可以生成全部方法的测试报告,如果测试良好则是绿色;如果测试失败,则是红色。

单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。

1.2单元测试快速入门

需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门。

分析:

① 将JUnit的jar包导入到项目中,IDEA通常整合好了Junit框架,一般不需要导入,如果IDEA没有整合好Junit框架,则在类空白处打出“@Test”并使用快速处理快捷键“Alt + Enter”,在网络中自动下载JUnit的jar包并自动“添加为库”,后可直接使用JUnit相关功能。

② 编写测试方法:该测试方法必须是公共的,无参数无返回值的非静态方法

③ 在测试方法上使用@Test注解,标注该方法是一个测试方法。

④ 在测试方法中完成被测试方法的预期正确性测试(不是必须的,若某个方法没有返回值,则无需断言,直接调用即可)。

⑤ 选中测试方法,选择“JUnit运行”,如果测试良好则是绿色;如果测试失败,则是红色;如果方法没有出现异常但结果与预期不同,则警告,颜色为黄色。

示例代码如下:

业务类

publicclassUserService {
publicStringloginName(StringloginName, StringpassWorld) {
if ("admin".equals(loginName) &&"123456".equals(passWorld)) retur"登陆成功";
elseretur"用户名或密码错误";
    }
publicvoidselectNames() {
System.out.println(10/0);
System.out.println("查询全部用户名成功!");
    }
}

测试类

publicclassTestUserService {
/*测试方法(每个方法都对应一个单独的测试方法)注:1.测试方法必须是公开的,无参数,无返回值的非静态方法2.测试方法必须使用@Test注解标记3.测试时,测试哪个方法就在哪个方法范围内,右键->run,在类中方法外右键->默认运行测试类中的全部测试方法,在module处右键->RuAllTests 可运行该模块中的全部测试方法*/@TestpublicvoidtestLoginName() {
UserServiceuserService=newUserService();
Stringrs=userService.loginName("admin", "123456");
// 预期结果正确性测试/*public static void assertEquals(String message, Object expected, Object actual)参数一:断言失败的提示信息参数二:断言返回值参数三:实际返回值*/Assert.assertEquals("loginName业务出现问题", "登陆成功", rs);
    }
@TestpublicvoidtestSelectNames() {
UserServiceuserService=newUserService();
userService.selectNames(); // selectNames()方法没有返回值,无需断言,直接调用即可    }
}

注:测试时,测试哪个方法就在哪个方法范围内,右键->run,在类中方法外右键->默认运行测试类中的全部测试方法,在module处右键->RuAllTests 可运行该模块中的全部测试方法。

Junit常用注解(Junit 4.xxxx版本)

注解(Junit 4.xxxx版本)

注解(Junit 5.xxxx版本)

说明

@Test

测试方法

@Before

@BeforeEach

用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次

@After

@AfterEach

用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次

@BeforeClass

@BeforeAll

用来静态修饰方法,该方法会在所有测试方法之前只执行一次

@AfterClass

@AfterAll

用来静态修饰方法,该方法会在所有测试方法之后只执行一次

 

开始执行的方法初始化资源。

执行完之后的方法释放资源。

2.反射

反射概述

反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。

在运行时,可以直接得到这个类的构造器对象:Constructor,成员变量对象:Field,成员方法对象:Method,这种运行时动态获取类的字节码文件对象以及动态调用类中全部成分的能力称为Java语言的反射机制

反射的关键

反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。

获取Class对象的三种方式

方式一:Class类中的一个静态方法:Class.forName(全限名:包名 + 类名(不加.class/.java))

方式二:类名.class

方式三:对象.getClass() 获取对象对应类的Class对象

反射获取构造器对象

Class类中用于获取构造器的方法

方法名

说明

Constructor<?>[] getConstructors ()

返回所有构造器对象的数组(只能拿public)

Constructor<?>[] getDeclaredConstructors ()

返回所有构造器对象的数组,存在就能拿到

Constructor<T> getConstructor (Class<?>... parameterTypes)

返回单个构造器对象(只能拿public的)

Constructor<T> getDeclaredConstructor (Class<?>... parameterTypes)

返回单个构造器对象,存在就能拿到

 

使用反射技术获取构造器对象并使用

Constructor类中用于创建对象的方法

方法名

说明

T newInstance(Object... initargs)

根据指定的构造器创建对象

public void setAccessible(booleaflag)

设置为true,表示取消访问检查,进行暴力反射用于打开私有构造器的访问权限

使用反射技术获取成员变量对象

Class类中用于获取成员变量的方法

方法名

说明

Field[] getFields()

返回所有成员变量对象的数组(只能拿public的)

Field[] getDeclaredFields()

返回所有成员变量对象的数组,存在就能拿到

Field getField(String name)

返回单个成员变量对象(只能拿public的)

Field getDeclaredField(String name)

返回单个成员变量对象,存在就能拿到

 

使用反射技术获取成员变量对象并使用

Field类中用于取值、赋值的方法

方法名

说明

void set(Object obj, Object value)

赋值

Object get(Object obj)

获取值

 

使用反射技术获取方法对象

Class类中用于获取成员方法的方法

方法名

说明

Method[] getMethods ()

返回所有成员方法对象的数组(只能拿public的)

Method[] getDeclaredMethods ()

返回所有成员方法对象的数组,存在就能拿到

Method getMethod (String name, Class<?>... parameterTypes)

返回单个成员方法对象(只能拿public的)

Method getDeclaredMethod (String name, Class<?>... parameterTypes)

返回单个成员方法对象,存在就能拿到

 

使用反射技术获取方法对象并使用

Method类中用于触发执行的方法

方法名

说明

Object invoke

(Object obj, Object... args)

运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值

(如果返回值没有就不写硬写的话,返回null

 

反射的作用-绕过编译阶段为集合添加数据

反射是作用在运行时的技术,此时集合的泛型将不能产生约束(泛型只约束编译阶段集合中的数据类型)了,此时是可以为集合存入其他任意类型的元素的。

泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList,泛型相当于被擦除了

示例代码如下:

publicclassReflectDemo {
publicstaticvoidmain(String[] args) throwsExceptio{
// 需求:反射实现泛型擦除后,加入其他类型的元素ArrayList<String>list1=newArrayList<>();
ArrayList<Integer>list2=newArrayList<>();
System.out.println(list1.getClass()); // class java.util.ArrayListSystem.out.println(list2.getClass()); // class java.util.ArrayListSystem.out.println(list1.getClass() ==list2.getClass()); // true 地址相同,说明是同一个Class对象ArrayList<Integer>list3=newArrayList<>();
list3.add(10);
list3.add(88);
//        list3.add("苏轼");Classc=list3.getClass();
Methodadd=c.getDeclaredMethod("add", Object.class);
add.invoke(list3, "苏轼");
System.out.println(list3); // [10, 88, 苏轼]    }
}

反射的作用-通用框架的底层原理

需求:给你任意一个对象,在不清楚对象类型的情况可以,可以把对象的字段名称和对应值存储到文件中去。

分析:

① 定义一个方法,可以接收任意类的对象。

② 每次收到一个对象后,需要解析这个对象的全部成员变量名称。

这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?

使用反射获取对象的Class类对象,然后获取全部成员变量信息。

遍历成员变量信息,然后提取本成员变量在该对象中的具体值

⑤ 存入成员变量名称和值到文件中去即可。

示例代码如下:

保存任意类型的对象工具类

publicclassMyBatisUtil {
/*** 保存任意类型的对象** @param obj*/publicstaticvoidsave(Objectobj) {
try (
PrintStreamps=newPrintStream(newFileOutputStream("day14_junit_reflect_annotation_proxy_app\\src\\com\\itheima\\d7_reflect_framework\\data.txt", true));
        ) {
// 1.提取对象的全部成员变量(只有反射可以解决)Classc=obj.getClass();
ps.println("==========="+c.getSimpleName() +"=============="); // c.getSimpleName() 获取当前类名  c.getName() 获取全限名:包名 + 类名// 2.提取全部成员变量Field[] fields=c.getDeclaredFields();
// 3.获取成员变量的信息for (Fieldfield : fields) {
field.setAccessible(true); // 暴力破解private类型变量StringfieldName=field.getName(); // 取名称// 取值(提取本成员变量在obj对象中的值)StringfieldValue=field.get(obj) +""; // 字符串拼接,使结果变为String类型(不能使用强转,使用强转时该变量真实类型必须是强转后的类型,否则报错)ps.println(fieldName+"="+fieldValue);
            }
        } catch (Exceptioe) {
e.printStackTrace();
        }
    }
}

测试类

publicclassReflectDemo {
publicstaticvoidmain(String[] args) {
Students=newStudent("王安石", '男', 50, "1班");
MyBatisUtil.save(s);
Teachert=newTeacher("孔丘", '男', 200000);
MyBatisUtil.save(t);
    }
}

程序运行结果如下:

data.txt文件内容:

===========Student==============

name=王安石

sex=男

age=50

className=1班

===========Teacher==============

name=孔丘

sex=男

salary=200000.0

注:反射的作用?

可以在运行时得到一个类的全部成分然后操作。

可以破坏封装性。(很突出)

也可以破坏泛型的约束性。(很突出)

更重要的用途是适合:做Java高级框架,基本上主流框架都会基于反射设计一些通用技术功能。

3.注解

3.1注解概述

注解概述

注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制,Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

注解的作用

Java类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

3.2自定义注解

自定义注解就是自己做一个注解来使用。

自定义注解格式如下图所示。

CustomAnnotationFormat.png

示例代码如下:

注解类

public@interfaceMyBook {
Stringname();
String[] authors();
doubleprice();
}
测试类(注解的使用位置以及赋初值)@MyBook(name="《活着》", authors="余华", price=50)
publicclassAnnotationDemo1 {
}

注:特殊属性:value属性

如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写,但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的(若其他多个属性都有default默认值,则value属性同样可以省略名称不写,直接写值)。

3.3元注解

概念:就是注解注解的注解。

元注解有两个:

@Target:约束自定义注解只能在哪些地方使用。

@Retention:申明注解的生命周期。

@Target中可使用的值定义在ElementType枚举类中,常用值如下:

TYPE:类,接口

FIELD:成员变量

METHOD:成员方法

PARAMETER:方法参数

CONSTRUCTOR:构造器

LOCAL_VARIABLE:局部变量

@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下:

SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在

CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.

RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

示例代码如下:

注解类

@Target({ElementType.METHOD, ElementType.FIELD})  // 元注解@Retention(RetentionPolicy.RUNTIME) // 一直存在,不会消亡public@interfaceMyTest {
}

3.4注解解析

注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。

与注解解析相关的接口

Annotation: 注解的顶级接口,注解都是Annotation类型的对象。

AnnotatedElement:该接口定义了与注解解析相关的解析方法。

AnnotatedElement定义的与注解解析相关的解析方法、

方法名

说明

Annotation[] getDeclaredAnnotations()

获得当前对象上使用的所有注解,

返回注解数组

T getDeclaredAnnotation(Class<T> annotationClass)

根据注解类型获得对应注解对象

boolean isAnnotationPresent(Class<Annotation> annotationClass)

判断当前对象是否使用了指定的注解,

如果使用了则返回true,否则返回false

 

注:所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口,他们都拥有解析注解的能力。

解析注解的技巧

注解在哪个成分上,我们就先拿哪个成分对象。

注解作用在类上,则要获得该类的Class对象,再来拿上面的注解。

注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解。

注解作用成员方法上,则要获得该成员方法对应的Method对象,再来拿上面的注解。

示例代码如下:

注解类

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceProduct {
Stringvalue(); // 产品名doubleprice() default100;
String[] manufacturer(); // 生产厂家}

商店类

@Product(value="钢笔", price=88, manufacturer=  {"得力", "回力"})
publicclassStore {
@Product(value="蓝球", manufacturer= {"李宁", "安踏"})
publicvoidtest(){}
}

测试类

publicclassAnnotationDemo3 {
// 类对象的注解解析@TestpublicvoidparseClass() {
// 1.得到类对象Classc=Store.class;
// 2.判断这个类上面是否存在这个注解if (c.isAnnotationPresent(Product.class)) {
// 3.直接获取该注解对象//            Annotation product = c.getDeclaredAnnotation(Product.class);// 要获取Product中的属性等,不能使用多态写法Productproduct= (Product) c.getDeclaredAnnotation(Product.class);
System.out.println(product.value());
System.out.println(product.price());
System.out.println(Arrays.toString(product.manufacturer()));
        }
    }
// 方法对象的注解解析@TestpublicvoidparseMethod() throwsException {
Classc=Store.class;
Methodm=c.getDeclaredMethod("test");
if (m.isAnnotationPresent(Product.class)) {
// 3.直接获取该注解对象//            Annotation product = c.getDeclaredAnnotation(Product.class);// 要获取Product中的属性等,不能使用多态写法Productproduct=m.getDeclaredAnnotation(Product.class);
System.out.println(product.value());
System.out.println(product.price());
System.out.println(Arrays.toString(product.manufacturer()));
        }
    }
}

3.5案例:模拟Junit框架

需求:定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行。

分析:

① 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在。

② 定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行。

示例代码如下:

注解类MyTest与3.3元注解中的示例代码注解类MyTest相同。

测试类

publicclassAnnotationDemo4 {
@MyTestpublicvoidtest1() {
System.out.println("===test1===");
    }
publicvoidtest2() {
System.out.println("===test2===");
    }
@MyTestpublicvoidtest3() {
System.out.println("===test3===");
    }
/*** 启动菜单,有注解的才会被调用** @param args*/publicstaticvoidmain(String[] args) throwsException {
AnnotationDemo4a=newAnnotationDemo4();
// 1.获取类对象Classc=AnnotationDemo4.class;
//        Class c = a.getClass();// 2.提取类对象中的全部成员方法Method[] methods=c.getDeclaredMethods();
// 遍历所有方法,若其有MyTest注解,则执行该方法for (Methodmethod : methods) {
if (method.isAnnotationPresent(MyTest.class)) {
method.invoke(a);
            }
        }
    }
}

程序运行结果如下:

===test1===

===test3===

4.动态代理

代理是什么?

代理是一个对象,用来对被代理对象的行为额外做一些辅助工作

在java中实现动态代理的要求是什么样的?

必须存在接口。

被代理对象需要实现接口。

使用Proxy提供的方法,获取代理对象。

   public static Object newProxyInstance(ClassLoader loader,

                                         Class<?>[] interfaces,

                                         InvocationHandler h)

参数一:要生成代理对象的对象的类的类加载器

参数二:要生成代理对象的对象的类实现的接口列表

参数三:代理的核心处理程序(将方法调用分派到的处理程序)

通过代理对象调用方法,执行流程是什么样的?

①先走向代理。

②代理可以为方法额外做一些辅助工作(真正方法执行前后)

③开始真正触发对象的方法的执行。

④回到代理中,由代理负责返回结果给方法的调用者。

具体执行流程见示例代码。

示例代码如下:

接口类

publicinterfaceSkill {
voidsing();
voiddance();
voidrap();
voidplayBasketball();
}

明星类(必须实现接口)

publicclassStarimplementsSkill{
privateStringname;
publicStar(Stringname) {
this.name=name;
    }
@Overridepublicvoidsing() {
System.out.println(this.name+"唱歌");
    }
@Overridepublicvoiddance() {
System.out.println(this.name+"跳舞");
    }
@Overridepublicvoidrap() {
System.out.println(this.name+"rap");
    }
@OverridepublicvoidplayBasketball() {
System.out.println(this.name+"打篮球");
    }
}

生成代理对象类

publicclassStarAgentProxy {
publicstaticSkillgetProxy(Starobj) {
// 为Cxk对象生成一个代理对象/***     public static Object newProxyInstance(ClassLoader loader,*                                           Class<?>[] interfaces,*                                           InvocationHandler h)*                                           参数一:要生成代理对象的对象的类的类加载器*                                           参数二:要生成代理对象的对象的类实现的接口列表*                                           参数三:代理的核心处理程序(将方法调用分派到的处理程序)*/return (Skill) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
newInvocationHandler() {
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
System.out.println("执行方法前的辅助工作");
// Method method:正在调用的method方法对象// Object[] args:method方法对象的参数Objectrs=method.invoke(obj, args);
System.out.println("执行方法后的首尾工作");
returnrs;
                    }
                });
    }
}

测试类

publicclassTest {
publicstaticvoidmain(String[] args) {
Stars=newStar("Cxk");
s.sing();
s.dance();
s.rap();
s.playBasketball();
System.out.println("=======代理对象=======");
// 为Cxk对象生成一个代理对象(经纪人)Skills2=StarAgentProxy.getProxy(s);
s2.sing();
s2.dance();
s2.rap();
s2.playBasketball();
    }
}

程序运行结果如下:

Cxk唱歌

Cxk跳舞

Cxkrap

Cxk打篮球

=======代理对象=======

执行方法前的辅助工作

Cxk唱歌

执行方法后的收尾工作

执行方法前的辅助工作

Cxk跳舞

执行方法后的收尾工作

执行方法前的辅助工作

Cxkrap

执行方法后的收尾工作

执行方法前的辅助工作

Cxk打篮球

执行方法后的收尾工作

案例-模拟企业业务功能开发,并完成每个功能的性能统计

需求:

①模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。

②使用动态代理的方式解决。

分析:

① 定义一个UserService表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。

② 定义一个实现类UserServiceImpl实现UserService,并完成相关功能,且统计每个功能的耗时。

③ 定义测试类,创建实现类对象,调用方法。

关键步骤

必须有接口,实现类要实现接口(代理通常是基于接口实现的)。

②创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。

本案例业务执行流程图如下图所示。

UserServiceProcess.png

示例代码如下:

接口类

publicinterfaceUserService {
Stringlogin(StringloginName, Stringpassword);
voiddeleteUsers();
StringselectUsers();
}

接口实现类

publicclassUserServiceImplimplementsUserService {
@OverridepublicStringlogin(StringloginName, Stringpassword) {
try {
Thread.sleep(1000);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
if ("admin".equals(loginName) &&"123456".equals(password)) return"登陆成功!";
return"登陆失败!";
    }
@OverridepublicvoiddeleteUsers() {
try {
Thread.sleep(2500);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
System.out.println("删除了1000条用户数据");
    }
@OverridepublicStringselectUsers() {
try {
Thread.sleep(1300);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
Stringrs="查询了500条数据";
returnrs;
    }
}

生成代理对象工具类

publicclassProxyUtil {
// 使用泛型,<T>代表泛型,可以传入任意类型的对象publicstatic<T>TgetProxy(Tobj) {
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
newInvocationHandler() {
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
longstartTime=System.currentTimeMillis();
Objectrs=method.invoke(obj, args); // 真正触发被代理对象的行为longendTime=System.currentTimeMillis();
System.out.println(method.getName() +"方法耗时:"+ (endTime-startTime) /1000.0+"s");
returnrs;
                    }
                });
    }
}

测试类

publicclassTest {
publicstaticvoidmain(String[] args) {
// 掌握使用动态代理解决问题,理解使用动态代理的优势UserServiceuserService=ProxyUtil.getProxy(newUserServiceImpl()); // 声明对象时,实现类对象可以向上转型为接口(多态)System.out.println(userService.login("admin", "123456"));
System.out.println(userService.selectUsers());
userService.deleteUsers();
    }
}

程序运行结果如下:

login方法耗时:1.005s

登陆成功!

selectUsers方法耗时:1.305s

查询了500条数据

删除了1000条用户数据

deleteUsers方法耗时:2.508s

动态代理的优点

非常的灵活,支持任意接口类型(使用泛型<T>)的实现类对象做代理,也可以直接为接本身做代理。

可以为被代理对象的所有方法做代理。

可以在不改变方法源码的情况下,实现对方法功能的增强。

不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。

相关文章
|
4天前
|
Java
PTA帅到没朋友(Java语言)+测试点
PTA帅到没朋友(Java语言)+测试点
11 1
|
4天前
|
Java 测试技术 数据库
【JAVA基础篇教学】第十七篇:Java单元测试
【JAVA基础篇教学】第十七篇:Java单元测试
|
1天前
|
存储 JavaScript Java
《手把手教你》系列技巧篇(四十七)-java+ selenium自动化测试-判断元素是否显示(详解教程)
【5月更文挑战第11天】WebDriver 的 `isDisplayed()` 方法用于检查页面元素是否可见,如果元素存在于DOM中且可视,返回`true`,否则返回`false`。在自动化测试中,这个方法常用于验证元素是否真正显示在页面上。示例代码展示了如何使用 `isDisplayed()` 判断百度登录页面的特定错误提示文字是否出现。
11 1
|
2天前
|
安全 Java API
JAVA-不安全的反射--RCE
JAVA不安全的反射造成的RCE小案例
|
2天前
|
JavaScript Java 测试技术
《手把手教你》系列技巧篇(四十六)-java+ selenium自动化测试-web页面定位toast-下篇(详解教程)
【5月更文挑战第10天】本文介绍了使用Java和Selenium进行Web自动化测试的实践,以安居客网站为例。最后,提到了在浏览器开发者工具中调试和观察页面元素的方法。
12 2
SpringJDK动态代理实现,2024Java面试真题精选干货整理
SpringJDK动态代理实现,2024Java面试真题精选干货整理
|
3天前
|
Web App开发 JavaScript 测试技术
《手把手教你》系列技巧篇(四十五)-java+ selenium自动化测试-web页面定位toast-上篇(详解教程)
【5月更文挑战第9天】本文介绍了在Appium中处理App自动化测试中遇到的Toast元素定位的方法。Toast在Web UI测试中也常见,通常作为轻量级反馈短暂显示。文章提供了两种定位Toast元素的技巧.
11 0
|
3天前
|
Java 测试技术
Java一分钟之-单元测试:JUnit与TestNG
【5月更文挑战第16天】本文介绍了Java常用的单元测试框架JUnit和TestNG,JUnit以其简洁注解受到青睐,而TestNG则提供更高级功能如参数化测试。常见问题包括测试未执行、断言失败等,解决办法包括检查项目配置、调整测试顺序。注意保持测试简单独立,确保高覆盖率。选择合适的框架可提升代码质量。
10 0
|
4天前
|
IDE Java 测试技术
Java测试三两事
Java测试三两事
10 1
|
4天前
|
Web App开发 缓存 前端开发
《手把手教你》系列技巧篇(四十四)-java+ selenium自动化测试-处理https 安全问题或者非信任站点-下篇(详解教程)
【5月更文挑战第8天】这篇文档介绍了如何在IE、Chrome和Firefox浏览器中处理不信任证书的问题。作者北京-宏哥分享了如何通过编程方式跳过浏览器的证书警告,直接访问不受信任的HTTPS网站。文章分为几个部分,首先简要介绍了问题背景,然后详细讲解了在Chrome浏览器中的两种方法,包括代码设计和运行效果,并给出了其他浏览器的相关信息和参考资料。最后,作者总结了处理此类问题的一些通用技巧。
16 2