本次实验的主要任务是完成期末案例的实体类开发和单元测试，并且熟悉IDEA工具的代码自动生成功能。

## 环境准备


{{< admonition warning >}}
每天的代码要保留一份单独的压缩包，命名示例：`day1.rar`
{{< /admonition >}}

1. 导入JUnit依赖（下方两个包放到lib目录）
2. 配置好MySQL数据库（参考Day 1）

[junit-4.13.2.jar](https://static.seahi.me/2024/12/junit-4.13.2.jar)
[hamcrest-core-1.3.jar](https://static.seahi.me/2024/12/hamcrest-core-1.3.jar)

{{< admonition quote 包说明>}}
1. junit-4.13.2.jar 是一个用来测试Java程序的工具
2. hamcrest-core-1.3.jar 是JUnit工具的依赖包
   {{< /admonition >}}

## 任务内容

### 1. 实体类开发

#### 1.1 请假申请实体类（LeaveRequest.java）

```java
package model;

import java.util.Date;

/**
 * 请假条实体类
 */
public class LeaveRequest {
    private int id;                  // 数据库主键ID
    private Student student;         // 学生信息
    private Date startTime;          // 开始时间
    private Date endTime;            // 结束时间
    private double duration;         // 时长
    private String location;         // 外出地址
    private String reasonType;       // 外出事由类型
    private String reasonDetail;     // 详细事由
    private boolean isLeavingCity;   // 是否离津
    private ApprovalStatus status;   // 审批状态
    private Teacher approver;        // 审批人
    private String approvalComment;  // 审批意见
    private Date requestTime;        // 申请时间
    private Date approvalTime;       // 审批时间

    // 构造方法
    // getter和setter方法
}
```

**操作步骤：**
1. 在`src/model`目录下创建`LeaveRequest.java`文件
2. 复制上述代码框架
3. 实现默认构造方法，设置初始状态为`PENDING`
4. 使用IDE**自动生成**所有属性的`getter`和`setter`方法

#### 1.2 审批状态枚举（ApprovalStatus.java）

> 该文件的代码是完整的，无需修改

```java
package model;

/**
 * 请假审批状态枚举类
 */
public enum ApprovalStatus {
    PENDING("待审批"),    // 等待审批
    APPROVED("已批准"),   // 已经批准
    REJECTED("已驳回");   // 已经驳回

    private final String description;  // 状态描述

    ApprovalStatus(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }
}
```

> enum是枚举类型，用于表示一组固定的常量。



{{< admonition >}}
类似地步骤，完成`Teacher`实体类和`Student`实体类
{{< /admonition >}}

#### 1.3 教师实体类（Teacher.java）

```java
package model;

/**
 * 教师实体类
 */
public class Teacher {
    private int id;              // 数据库主键ID
    private String teacherId;    // 教师工号
    private String name;         // 教师姓名
    private String department;   // 所属部门
    private String contact;      // 联系方式
    private String password;     // 登录密码

	// 使用 IDE 自动生成以下内容：
    // 构造方法
    // getter 和 setter 方法
}
```
### 2. 单元测试开发

**什么是单元测试？**

单元测试是检查程序中最小单位（一个方法、一个功能）是否正确运行的测试。

**为什么要做单元测试?**
- 及早发现程序中的错误
- 确保修改代码后，原有功能仍然正常
- 节省手工测试的时间

**单元测试的优点**
- 自动化：写好测试代码后可以反复运行
- 快速：一次能测试多个功能点
- 可靠：比人工测试更准确

在实际开发中，单元测试是保证代码质量的重要手段。以后你们进入公司工作，很可能会遇到需要写单元测试的要求。对Java代码进行单元测试，常用的工具（包）是Junit

{{< admonition success 注意>}}
本次任务需要了解单元测试的意义、方法，后期的单元测试代码会给同学们提供完整代码。
{{< /admonition >}}

#### 2.1 创建测试目录

1. 在`src`目录下创建`test`目录
2. 在`test`目录下创建`model`子目录
3. 在`test/model`目录下创建各个测试类

#### 2.2 编写Teacher测试类（TeacherTest.java）

```java
package test.model;

import model.Teacher;
import org.junit.Test;
import static org.junit.Assert.*;

public class TeacherTest {

    @Test
    public void testConstructorAndGetters() {
        String teacherId = "T001";
        String name = "张老师";
        String department = "计算机系";
        String contact = "13800138000";
        String password = "password123";

        Teacher teacher = new Teacher(teacherId, name, department, contact, password);

        assertEquals("教师工号应匹配", teacherId, teacher.getTeacherId());
        assertEquals("教师姓名应匹配", name, teacher.getName());
        assertEquals("所属部门应匹配", department, teacher.getDepartment());
        assertEquals("联系方式应匹配", contact, teacher.getContact());
        assertEquals("密码应匹配", password, teacher.getPassword());
    }

    @Test
    public void testSetters() {
        Teacher teacher = new Teacher();

        int id = 1;
        String teacherId = "T002";
        String name = "李老师";
        String department = "数学系";
        String contact = "13900139000";
        String password = "newpass123";

        teacher.setId(id);
        teacher.setTeacherId(teacherId);
        teacher.setName(name);
        teacher.setDepartment(department);
        teacher.setContact(contact);
        teacher.setPassword(password);

        assertEquals("ID应匹配", id, teacher.getId());
        assertEquals("教师工号应匹配", teacherId, teacher.getTeacherId());
        assertEquals("教师姓名应匹配", name, teacher.getName());
        assertEquals("所属部门应匹配", department, teacher.getDepartment());
        assertEquals("联系方式应匹配", contact, teacher.getContact());
        assertEquals("密码应匹配", password, teacher.getPassword());
    }
}
```

**操作步骤：**
1. 创建TeacherTest.java文件
2. 编写构造方法测试用例
3. 编写getter/setter方法测试用例

#### 2.4 编写DatabaseUtil测试类（DatabaseUtilTest.java）

```java
package test.util;

import org.junit.Test;
import util.DatabaseUtil;

import java.sql.Connection;
import java.sql.SQLException;

import static org.junit.Assert.*;

public class DatabaseUtilTest {

    @Test
    public void testGetConnection() {
        try (Connection conn = DatabaseUtil.getConnection()) {
            // 验证连接不为空
            assertNotNull("数据库连接不应该为空", conn);
            // 验证连接是否有效
            assertTrue("数据库连接应该是有效的", !conn.isClosed());
        } catch (SQLException e) {
            fail("获取数据库连接时发生异常: " + e.getMessage());
        }
    }

    @Test
    public void testConnectionAutoClose() {
        Connection conn = null;
        try {
            // 在try-with-resources块中获取连接
            try (Connection autoCloseConn = DatabaseUtil.getConnection()) {
                assertNotNull("数据库连接不应该为空", autoCloseConn);
                assertTrue("连接应该是开启状态", !autoCloseConn.isClosed());
            }
            // try-with-resources块结束后，获取一个新连接来测试
            conn = DatabaseUtil.getConnection();
            assertNotNull("新的数据库连接不应该为空", conn);
            assertTrue("新的连接应该是开启状态", !conn.isClosed());
        } catch (SQLException e) {
            fail("测试连接自动关闭时发生异常: " + e.getMessage());
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
```

**操作步骤：**
1. 在test.util包下创建DatabaseUtilTest.java文件
2. 编写测试数据库连接的方法testGetConnection()
3. 编写测试连接自动关闭的方法testConnectionAutoClose()
4. 确保使用try-with-resources语句来自动管理数据库连接资源

## 完成标准

- [ ] 所有实体类完整实现
  - [ ] Teacher类（属性、构造方法、getter/setter）
  - [ ] Student类（属性、构造方法、getter/setter）
  - [ ] LeaveRequest类（属性、构造方法、getter/setter）
  - [ ] ApprovalStatus枚举（所有状态值和描述）
- [ ] 所有单元测试通过
  - [ ] TeacherTest全部测试用例通过
  - [ ] DatabaseUtilTest全部测试用例通过
- [ ] 代码规范
  - [ ] 适当的注释说明
  - [ ] 规范的命名
  - [ ] 合理的代码组织

## 编码规范

1. 类名使用大驼峰命名法（如：`TeacherTest`）
2. 方法名使用小驼峰命名法（如：`testConstructor`）
3. 变量名使用小驼峰命名法（如：`teacherId`）
4. 常量使用全大写（如：`PENDING`）
5. 每个类都要有类注释
6. 重要方法要有方法注释

## 测试运行方法

1. 在IDE中右键点击test包
2. 选择"Run 'Tests in test'" 运行所有测试类
3. 或右键点击test内的某个包，选择"Run 'Tests in 包名'"运行包内所有测试
4. 或右键点击单个测试类，选择"Run 'Tests in 类名'"运行单个测试类
5. 查看测试结果：绿色√表示通过，红色×表示失败


## 提示与建议

1. 使用IDE的代码生成功能可以快速生成getter/setter方法
2. 测试用例要考虑各种可能的情况
3. 测试失败时要仔细阅读错误信息
4. 定期运行所有测试，确保修改不会影响其他功能

