本次实验的主要任务是完成期末案例的实体类开发和单元测试,并且熟悉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. 定期运行所有测试,确保修改不会影响其他功能