程序测试对保障应用程序来说,其重要性不可忽视。JUnit是我们必须掌握的测试框架,大多数测试框架和工具都是在此基础上扩展而来。直接使用JUnit测试基于Spring的应用存在诸多不便,我们需要花费大量的经理去应付测试现场恢复、访问测试数据操作结果等工作。但是DbUnit的出现,这些问题有了很好的解决方案,DbUnit对测试DAO层提供了强大的支持,大大提高了编写测试用例的效率和质量。(文章文字摘自《Spring3.X企业应用开发实践--陈雄华、林开雄 著》)
1. 单元测试的好处
编写代码的过程中,总是反复调试保证能编译通过,但是代码编译通过,只能说明语法正确,无法保证逻辑是正确的。单元测试可以作保证,有了单元测试,可以自信地提交代码,减少后顾之忧。带来好处如下:
· 是软件质量最简单、最有效的包装;
· 是目标代码最清晰的、最有效的文档;
· 可以优化目标代码的设计
· 是代码重构的保障;
· 是回归测试和持续集成的基石
2.单元测试的误解
- 误解一:影响开发进度
一旦编码完成,开发人员总数迫切的希望进行软件的集成工作,这样就可以看到系统实际运行效果。而单元测试这样的活动,就被看成是影响进度的原因之一,推迟整个系统进行集成测试的时间。但是这样一意孤行导致的结果经常是:软件无法运行,更进一步说,编程人员不得不花费大量的时间去追踪这些隐藏在独立单元内简单的BUG上,有些有报错信息还好说,但是有些属于逻辑错误,这是机器无法通知编程人员的。
在实际工作中,进行完整计划的单元测试和编写实际的代码所花费的时间和精力应该是一样的。一旦完成单元测试工作,许多BUG都被纠正了,开发人员也就能进行系统集成工作。
- 误解二:增加开发成本
如果不重视程序中那些未被发现的BUG可能带来的后果,严重程度可以从一个BUG引起的用户体验不适到系统崩溃。这种后果可能被软件开发人员忽视(PS:毕竟很多公司都是测试和开发是同一人做的,同时做两份工作换我也不开心:( ),BUG发现的越早,维护的费用越少。对比复杂而旷日持久的集成测试来说,单元测试所需的费用是很低的。
- 误解三:用System.out.print跟踪和运行程序就够了
很多懒人使用Ssytem.out.print来输出结果,判断程序代码是否正确,不想写测试用例,除了懒,另外就是没有时间,但是用这种方式输出结果来进行测试,不仅效率低下,而且容易发生错误、
- 误解四:存在太多无法测试的东西
在编码的时候,确实存在一些看起来比较难测试的代码,但是有些并不是无法测试,大多数时候主要是前期做系统设计的时候没有考虑到测试方面的问题,导致内部耦合过紧,过于依赖运行环境,进而表现出代码很难被测试。
- 误解五:测试代码随意写
编写测试代码的时候总会抱着那些随意的态度,没去弄清测试的意图,编写测试代码是为了应付人员而已,而编写的内容和测试根本八辈子打不到一块,这样子的单元测试跟没测试没什么区别。
3. 单元测试案例(DbUnit测试)
- 准备~开始
测试工具是由孔浩老师的视频处的来的,各位可以修改下然后应用到自己的项目内
测试环境:
GitHub查看
测试Bean:Branch
@Entity
@Table(name = "branch")
public class Branch {
private int id;
private String name;
private Subject subject;
public Branch() {
}
public Branch(int id, String name) {
this.id = id;
this.name = name;
}
public Branch( int id, String name,Subject subject) {
this.id = id;
this.name = name;
this.subject = subject;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne
@JoinColumn(name = "sub_id",referencedColumnName = "id")
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
4.测试DAO:BranchDao(其它方法略)
@Repository("branchDao")
public class BranchDao extends BaseDao
@Override
public Branch loadByName(String name) {
String hql="select branch from Branch branch where branch.name=?";
return (Branch)this.queryObject(hql,name);
}
}
测试的数据库生成文件
单元测试类:BranchDaoTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/beans.xml")
public class BranchDaoTest extends AbstractDbUnitTestCase {
@Inject
private SessionFactory sessionFactory;
@Inject
private IBranchDao branchDao;
@Inject
private ISubjectDao subjectDao;
@Before
public void setUp() throws SQLException, IOException, DatabaseUnitException {
//此时最好不要使用Spring的Transactional来管理,因为dbunit是通过jdbc来处理connection,再使用spring在一些编辑操作中会造成事务shisu
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
this.backupAllTable();
IDataSet ds = createDateSet("t_beans");
DatabaseOperation.CLEAN_INSERT.execute(dbunitCon,ds);
}
@After
public void tearDown() throws DatabaseUnitException, SQLException, IOException {
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
this.resumeTable();
}
@Test
public void testLoadByName(){
Branch branch=branchDao.loadByName("测试一");
assertEquals(branch.getId(), 1);
}
}
测试结果