#FormatImgID_6#
|
3. 如何编写单元测试
在XP下强调单元测试必须由类包的编写者负责编写,这个限定对于我们设定的测试目标是必须的。因为只有这样,测试才能保证对象的运行时态行为符合需求,而仅通过类接口的测试,我们只能确保对象符合静态约束,因此这就要求我们在测试的过程中,必须开放一定的内部数据结构,或者针对特定的运行行为建立适当的数据记录,并把这些数据暴露给特定的测试单元。这也就是说我们在编写单元测试时必须对相应的类包进行修改,这样的修改也发生在我们以前使用的测试方法中,因此以前的测试标记及其他一些测试技巧仍然可以在Junit测试中改进使用。
由于单元测试的总体目标是负责我们的软件在运行过程中的正确无误,因此在我们对一个对象编写单元测试的时候,我们不但需要保证类的静态约束符合我们的设计意图,而且需要保证对象在特定的条件下的运行状态符合我们的预先设定。还是拿数据库缓冲池的例子说明,一个缓冲池暴露给其他对象的是一组使用接口,其中包括对池的参数设定、池的初始化、池的销毁、从这个池里获得一个数据连接以及释放连接到池中,对其他对象而言随着各种条件的触发而引起池的内部状态的变化是不需要知道的,这一点也是符合封装原理的。但是池对象的状态变化,譬如:缓存的连接数在某些条件下会增长,一个连接在足够长的运行后需要被彻底释放从而使池的连接被更新等等,虽然外部对象不需要明确,但是却是程序运行正确的保证,所以我们的单元测试必须保证这些内部逻辑被正确的运行。
编译语言的测试和调试是很难对运行的逻辑过程进行跟踪的,但是我们知道,无论逻辑怎么运行,如果状态的转换符合我们的行为设定,那验证结果显然是正确的,因此在对一个对象进行单元测试的时候,我们需要对多数的状态转换进行分析和对照,从而验证对象的行为。状态是通过一系列的状态数据来描述的,因此编写单元测试首先分析出状态的变化过程(状态转换图对这个过程的描述非常清晰),然后根据状态的定义确定分析的状态数据,最后是提供这些内部的状态数据的访问。在数据库连接池的例子中,我们对池实现的对象DefaultConnectionProxy的状态变换进行分析后,我们决定把表征状态的OracleConnectionCacheImpl对象公开给测试类。参见示例一
示例一 /** * 这个类简单的包装了oracle对数据连接缓冲池的实现。 * */ public class DefaultConnectionProxy extends ConnectionProxy { private static final String name = "Default Connection Proxy"; private static final String description = "这个类简单的包装了oracle对数据连接缓冲池的实现。"; private static final String author = "Ion-Global.com"; private static final int major_version = 0; private static final int minor_version = 9; private static final boolean pooled = true; private ConnectionBroker connectionBroker = null; private Properties props; private Properties propDescriptions; private Object initLock = new Object(); // Test Code Begin... /* 为了能够了解对象的状态变化,因此需要把表征对象内部状态变化的部分私有变量提供公共的访问接口 (或者提供让同一个类包的访问接口),以便使测试单元可以有效地判断对象的状态转变, 在本示例中对包装的OracleConnectionCacheImpl对象提供访问接口。 */ OracleConnectionCacheImpl getConnectionCache() { if (connectionBroker == null) { throw new IllegalStateException("You need start the server first."); } return connectionBroker.getConnectionCache(); } // Test Code End... 在公开内部状态数据後,我们就可以编写我们的测试单元了,单元测试的选择方法和选择尺度已经在本文前面章节进行了说明, 但是仍然需要注意的是,由于assert方法会抛出一个error,你应该在测试方法的最后集中用assert相关方法进行判断, 这样可以确保资源得到释放。 对数据库连接池的例子,我们可以建立测试类DefaultConnectionProxyTest,同时建立数个test case,如下 示例二 /** * 这个类对示例一中的类进行简单的测试。 |