Junit单元测试,反射,注释

Junit单元测试

JUnit是一个用于编写和运行单元测试的Java框架。它为开发人员提供了一种简单且有效的方式来验证他们的代码是否按照预期工作。

下面是编写JUnit单元测试的一般步骤:

  1. 导入JUnit库:首先,在项目中导入JUnit库。可以使用构建管理工具(如Maven或Gradle)添加JUnit依赖项。

  2. 创建测试类:创建一个新的Java类,命名为TestXXX(XXX表示要测试的类名)。在该类中,使用@Test注解标记要进行测试的方法。

  3. 编写测试方法:在测试类中,编写测试方法来验证代码的不同方面。测试方法应该是公共(public)、无返回值(void)且没有参数。使用各种断言方法(如assertEquals()assertTrue()等)来检查代码的输出是否与预期相符。

  4. 运行测试:使用JUnit运行测试。可以在集成开发环境(IDE)中右键单击测试类并选择"Run as JUnit test",也可以使用构建工具的命令行界面运行测试。

  5. 查看结果:JUnit将显示每个测试方法的执行结果。如果所有测试通过,则意味着代码按预期工作。如果有失败的测试,JUnit将显示详细的错误消息,帮助您找到问题所在。

重复这些步骤,编写更多的测试方法来覆盖代码的各个方面和边界情况。这样可以确保代码在不同情况下都能正确运行,并提高代码的质量和可靠性。

JUnit是java自带的包下吗

是的,JUnit是Java自带的包。它位于javajavax包下的org.junit子包中。在Java开发环境中,默认情况下会包含JUnit库,因此您可以直接使用JUnit进行单元测试。

在Java 11及更早的版本中,JUnit 4是默认的JUnit版本。而在Java 12及之后的版本中,默认的JUnit版本为JUnit 5(也被称为JUnit Jupiter)。无论是JUnit 4还是JUnit 5,都可以通过导入org.junit包来使用。

请注意,尽管JUnit是Java自带的包,但有时您可能需要手动将JUnit库添加到您的项目中。这通常是通过构建管理工具(如Maven、Gradle或Ant)来完成的,以便能够在项目中使用JUnit的特定版本。

白盒黑盒,before after, 断言

* 测试分类:
    1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
    2. 白盒测试:需要写代码的。关注程序具体的执行流程。

* Junit使用:白盒测试
    * 步骤:
        1. 定义一个测试类(测试用例)
            * 建议:
                * 测试类名:被测试的类名Test        CalculatorTest
                * 包名:xxx.xxx.xx.test        cn.itcast.test

        2. 定义测试方法:可以独立运行
            * 建议:
                * 方法名:test测试的方法名        testAdd()  
                * 返回值:void
                * 参数列表:空参

        3. 给方法加@Test
        4. 导入junit依赖环境

    * 判定结果:
        * 红色:失败
        * 绿色:成功
        * 一般我们会使用断言操作来处理结果
            * Assert.assertEquals(期望的结果,运算的结果);

    * 补充:
        * @Before:
            * 修饰的方法会在测试方法之前被自动执行
        * @After:
            * 修饰的方法会在测试方法执行之后自动被执行

反射

java反射是一种机制

它允许程序在运行时检查、访问和操作类、对象、方法和字段等。通过反射,我们可以动态地获取和使用在编译时未知的类和成员信息。简单来说,反射就是在程序执行期间对类进行检查和操作的能力。

* 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
* 反射:将类的各个组成部分封装为其他对象,这就是反射机制
    * 好处:
        1. 可以在程序运行过程中,操作这些对象。
        2. 可以解耦,提高程序的可扩展性。

要使用反射,可以通过以下步骤:

  1. 获取Class对象:可以使用类名.class语法或者Class.forName("类名")方法获取类的Class对象。
  2. 创建对象:可以使用Class对象的newInstance()方法创建类的实例。
  3. 调用方法:可以使用Method类的invoke()方法来调用方法,需要提供方法名和参数类型。
  4. 访问字段:可以使用Field类的get()和set()方法来读取和修改字段的值,需要提供字段名。

需要注意的是,反射操作可能会降低性能并增加代码的复杂性,因此应该谨慎使用。另外,反射访问私有成员需要取消访问权限检查,可能存在安全风险。

* 获取Class对象的方式:
    1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
    2. 类名.class:通过类名的属性class获取
        * 多用于参数的传递
    3. 对象.getClass():getClass()方法在Object类中定义着。
        * 多用于对象的获取字节码的方式

    * 结论:
        同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

* Class对象功能:
    * 获取功能:
        1. 获取成员变量们
            * Field[] getFields() :获取所有public修饰的成员变量
            * Field getField(String name)   获取指定名称的 public修饰的成员变量

            * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
            * Field getDeclaredField(String name)  
        2. 获取构造方法们
            * Constructor<?>[] getConstructors()  
            * Constructor<T> getConstructor(类<?>... parameterTypes)  

            * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
            * Constructor<?>[] getDeclaredConstructors()  
        3. 获取成员方法们:
            * Method[] getMethods()  
            * Method getMethod(String name, 类<?>... parameterTypes)  

            * Method[] getDeclaredMethods()  
            * Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

        4. 获取全类名    
            * String getName()  

* 案例: * 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法 * 实现: 1. 配置文件 2. 反射 * 步骤: 1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中 2. 在程序中加载读取配置文件 3. 使用反射技术来加载类文件进内存 4. 创建对象 5. 执行方法

// 配置文件 config.properties
# 需要创建的对象的全类名和方法名
className=com.example.MyClass
methodName=doSomething

// 主程序 Main.java
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {
    public static void main(String[] args) throws Exception {
        // 加载配置文件
        InputStream input = Main.class.getClassLoader().getResourceAsStream("config.properties");
        Properties properties = new Properties();
        properties.load(input);

        // 读取配置文件中的类名和方法名
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        // 使用反射加载类文件
        Class<?> clazz = Class.forName(className);

        // 创建对象
        Object obj = clazz.newInstance();

        // 获取方法对象
        Method method = clazz.getMethod(methodName);

        // 执行方法
        method.invoke(obj);
    }
}

// 被调用的类 MyClass.java
public class MyClass {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

注解

在Java中,注解(Annotation)是一种元数据,它可以被添加到类、方法、变量等程序元素上,用于提供额外的信息和指示。注解使用@符号作为前缀,并紧跟着注解的名称。

注解在编译时、运行时或两者都可以被读取和处理。通过使用注解,我们可以为代码添加标记、配置参数、生成文档等。

Java提供了一些内置的注解,例如@Override用于标记一个方法重写了父类的方法,@Deprecated用于标记已过时的方法或类,@SuppressWarnings用于抑制编译器警告等。

除了内置注解,我们还可以创建自定义注解。自定义注解需要使用@interface关键字进行定义。自定义注解可以定义成员变量,也可以包含默认值。我们可以通过反射机制来获取并处理自定义注解。

注解在Java开发中广泛应用,例如在Spring框架中,注解用于声明依赖注入、事务管理等;在JUnit测试框架中,注解用于标记测试方法等。通过使用注解,我们可以更灵活和方便地进行代码开发和配置。

* 概念:说明程序的。给计算机看的
* 注释:用文字描述程序的。给程序员看的

* 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
* 概念描述:
    * JDK1.5之后的新特性
    * 说明程序的
    * 使用注解:@注解名称

* 作用分类:
    ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
    ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

* JDK中预定义的一些注解
    * @Override    :检测被该注解标注的方法是否是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过时
    * @SuppressWarnings:压制警告
        * 一般传递参数all  @SuppressWarnings("all")

* 自定义注解
    * 格式:
        元注解
        public @interface 注解名称{
            属性列表;
        }

    * 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
        * public interface MyAnno extends java.lang.annotation.Annotation {}

    * 属性:接口中的抽象方法
        * 要求:
            1. 属性的返回值类型有下列取值
                * 基本数据类型
                * String
                * 枚举
                * 注解
                * 以上类型的数组

            2. 定义了属性,在使用时需要给属性赋值
                1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
                2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
                3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
    

//定义注解属性的语法如下:
public @interface MyAnnotation {
    dataType attributeName() default defaultValue;
}
/**
*其中,dataType表示属性的类型,attributeName表示属性的名称, 
* defaultValue表示属性的默认值(可**** 选)。
*
*例如,以下是一个自定义注解MyAnnotation,其中包含两个属性:
*/
public @interface MyAnnotation {
    String name(); // 字符串类型的属性名

    int age() default 18; // 整数类型的属性age,默认值为18
}
//在使用自定义注解时,我们可以为属性提供值,也可以使用属性的默认值,例如:
@MyAnnotation(name = "John", age = 25)
public class MyClass {
    // 类的内容
}

//在上述示例中,我们为name属性提供了值"John",而为age属性提供了值25。如果没有为属性提供值,则会使
//用属性的默认值。

//通过反射机制,我们可以在运行时获取注解属性的值,并根据属性值进行相应的处理。

元注解(Meta-Annotation)是指用于注解其他注解的注解。Java提供了几个元注解,用于对自定义注解进行修饰和限制。

以下是Java中常见的元注解:

  1. @Target:用于指定注解可以应用的目标元素类型,包括TYPE(类、接口、枚举)、FIELD(字段)、METHOD(方法)、PARAMETER(方法参数)、CONSTRUCTOR(构造函数)等。

  2. @Retention:用于指定注解的保留策略,即注解在何时有效。包括SOURCE(仅在源代码阶段有效)、CLASS(在编译时有效,默认值)和RUNTIME(在运行时有效,可通过反射获取注解信息)。

  3. @Documented:用于指定注解是否包含在Java文档中。

  4. @Inherited:用于指示注解是否可以被继承,默认情况下,注解不会被子类继承。

除了这些常见的元注解,还有一些其他的元注解,如@Repeatable(指定注解是否可以重复应用于同一目标元素)和@Native(用于标记本地方法)等。

元注解提供了更多的控制和约束,使得我们可以更精确地定义和使用自定义注解。通过合理使用元注解,我们可以为自定义注解添加更多的语义和限制。

案例:

以下是一个使用注解来检测字段是否为空、是否含有空格的Java代码示例:

首先,定义一个注解类@FieldCheck,其中包含两个属性:allowEmptyallowWhitespace,默认值都为false

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldCheck {
    boolean allowEmpty() default false;
    boolean allowWhitespace() default false;
}

接下来,创建一个实体类Person,并在需要进行检测的字段上添加@FieldCheck注解。

public class Person {
    @FieldCheck(allowEmpty = false, allowWhitespace = false)
    private String name;

    // getter and setter
}

最后,编写一个工具类FieldChecker,用于检测带有@FieldCheck注解的字段是否满足要求。

import java.lang.reflect.Field;

public class FieldChecker {
    public static void checkFields(Object obj) throws IllegalAccessException {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
               //检查当前字段是否被注解@FieldCheck标注。
            if (field.isAnnotationPresent(FieldCheck.class)) {
                field.setAccessible(true);
              //获取字段上的@FieldCheck注解实例。
                FieldCheck annotation = field.getAnnotation(FieldCheck.class);
              ///获取当前字段在给定对象obj中的值
                Object value = field.get(obj);

                if (value == null || (annotation.allowEmpty() && value.toString().isEmpty())
                        || (annotation.allowWhitespace() && value.toString().trim().isEmpty())) {
                    throw new IllegalArgumentException("Field '" + field.getName() + "' is empty or contains whitespace.");
                }
            }
        }
    }
}

现在,我们可以使用FieldChecker类来检测Person对象的字段是否满足注解约定:

public class Main {
    public static void main(String[] args) throws IllegalAccessException {
        Person person = new Person();
        person.setName("John Doe");

        // 检测字段是否为空或包含空格
        FieldChecker.checkFields(person);
    }
}

如果name字段为空或者包含空格,将抛出IllegalArgumentException异常。

通过这样的实现,我们可以使用注解来简化字段的检测逻辑,并提高代码的可读性和可维护性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值