老规矩, 测试先行, 先分析测试Case:

 

MockLog4jAppenderTest类:
主要测试MockLog4jAppender的用法, 这是类实在是很简单, 就在这儿分析了.
// 这个类继承了AppenderSkeleton, 主要是
// 实现AppenderSkeleton的append函数, 将log事件加入到内部的List   

看一下整体流程, 看看Log4j是如何工作的.
通过debug, 我们发现log4j找logger是根据配置文件里命名来找的,
比如:
log4j.rootLogger=WARN, Console
log4j.logger.org.springside.modules=INFO

//虽然找到了, 但是找的是一部分, 那么就创建一个新的logger,
//并把rootlogger给他, 并把新创建的logger放到池中, 并且更新一下Logger的hierarchy
Logger.getLogger("org.springside"); 
//没有找到了,  那么就创建一个新的logger,  并把rootlogger给他
//并把新创建的logger放到池中
Logger.getLogger("xx.xxx"); 
只有完全找到了, 才把配置文件中的属性赋给这个Logger.

现在将展示一下log4j层次的logger:    
配置文件如下:
log4j.rootLogger=WARN, Console  
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p %-30.30c %X{traceId}-%m%n
log4j.logger.org.springside.modules=INFO

测试代码如下:
String testString1 = "Hello";
        String testString2 = "World";   
        //新创建一个appender, 用来记录事件的.
        MockLog4jAppender appender = new MockLog4jAppender();  
        //"org.springside"这个logger不存在, 则创建一个新的, 并更新一下层次关系
        // 当前的层次关系:
        // ->name= "org.springside.modules"   
        // ->name= "org.springside"   
        // ->name= "root"     
        // 此时, 这个MockLog4jAppender是附着在org.springside级别的logger上的.
        appender.addToLogger("org.springside");
   
    //这个还是找不到, 但是由于这个MockLog4jAppenderTest是在org.springside.modules包下的
    //所以, 层级关系变成:
    // name= "org.springside.modules.unit.log.MockLog4jAppenderTest"   
    // ->name= "org.springside.modules"   
    // ->name= "org.springside"   
    // ->name= "root"   
        Logger logger = LoggerFactory.getLogger(MockLog4jAppenderTest.class);    
       
        //这一步是关键:
        // 处理过程就是从最低级别的logger的appender开始处理, 一直走到最高级root的appender
        // 这也就是为什么一个什么都没有配置的logger, 却可以在控制台上输出日志,
        // 因为它使用了root的appender. 这就是log4j的层级控制关系.
        logger.warn(testString1);
        logger.warn(testString2);
        assertEquals(testString1, appender.getFirstLog().getMessage());

        assertEquals(testString2, appender.getLastLog().getMessage());
        assertEquals(2, appender.getAllLogs().size());
        assertEquals(testString2, appender.getAllLogs().get(1).getMessage());

        appender.clearLogs();
        assertNull(appender.getFirstLog());

第一个测试case就分析完了, 并且顺带把MockLog4jAppender也搞定了.

再看一下测试包下的utils包:
EncodeUtilsTest类:
这个类主要是测试EncodeUtils类的, EncodeUtils类就不分析了..以后就直接放在工具包中调用好了.
感觉这个可以用来简单加密一下查询条件等get参数的信息.

SpringContextHolderTest类:
主要测试SpringContextHolder类, 这个类
以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.

由于这个类也比较简单, 所以也顺手分析一下SpringContextHolder:
实现了ApplicationContextAware, DisposableBean接口:
ApplicationContextAware接口保证了SpringContextHolder定义在spring的配置文件的时候, 当spring容器启动,
SpringContextHolder可以自动持有ApplicationContext.

可以看到, 当spring容器初始化的时候, 所有实现了ApplicationContextAware接口的类都会获得一个ApplicationContext,
这是一个经典的观察者模式.

之后后面就很简单了, getBean方法也就是简单从ApplicationContext中取得一个bean. 值得注意的是泛型方法的写法.
public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }


ReflectionUtilsTest测试类:
主要测试ReflectionUtils工具类的, 不打算深入了解, 以后拿来当做工具用. 
//无需getter函数, 直接读取privateField
ReflectionUtils.getFieldValue(bean, "privateField") 
//绕过将publicField+1的getter函数,直接读取publicField的原始值
ReflectionUtils.getFieldValue(bean, "publicField")

//无需setter函数, 直接设置privateField
ReflectionUtils.setFieldValue(bean, "privateField", 2);

//触发get方法
ReflectionUtils.invokeGetterMethod(bean, "publicField");
//触发普通private方法方法
ReflectionUtils.invokeMethod(bean, "privateMethod", new Class[] { String.class },    new Object[] { "calvin" })
//获取第1,2个泛型类型
ReflectionUtils.getSuperClassGenricType(TestBean.class)   
ReflectionUtils.getSuperClassGenricType(TestBean.class, 1)

// 这个Lists是Google的包, 使用对象初始化数组元素
List list = Lists.newArrayList(bean1, bean2); 
// 把数组元素的id属性用","拼接
ReflectionUtils.convertElementPropertyToString(list, "id", ",")            

//把需要抓取的Exception转化成不需要抓取的Exception
Exception ex = new Exception();
RuntimeException        e = ReflectionUtils
                .convertReflectionExceptionToUnchecked(new InvocationTargetException(
                        ex));     

最后需要研究的就是最困难的线程工具测试类:ThreadUtilTest
测试方法customizableThreadFactory: 
//初始化一个线程任务
Runnable runnable = new Runnable() {
            @Override
            public void run() {
            }
        };
        CustomizableThreadFactory factory = new CustomizableThreadFactory("foo");
   
    //使用CustomizableThreadFactory来产生一个线程
        Thread thread = factory.newThread(runnable);
        assertEquals("foo-1", thread.getName());

        Thread thread2 = factory.newThread(runnable);
        assertEquals("foo-2", thread2.getName());           
       
再来看orm下的测试类:
PageTest类: 主要为了测试Page这个对象, 这个对象:
与具体ORM实现无关的分页参数及查询结果封装. 注意所有序号从1开始.
主要测试点在于: 是否需要向前翻页, 向后翻页, 排序等.

看一下orm下的hibernate包的测试类:    

//@MappedSuperclass 用在实体的继承过程中的父类上;
//由于没有针对该超类本身的表存在,因此这些映射只适用于它的子类。
@MappedSuperclass
public abstract class IdEntity {

    protected Long id;
 
  //使用 @Id 批注将一个或多个持久字段或属性指定为实体的主键。
    @Id
    //用来生成主键的值
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

// 使用 @Entity 批注将普通的旧式 Java 对象 (POJO) 类指定为实体
@Entity   
// @Table 批注指定与实体关联的主表
// 默认情况, 数据表名和对象名一致
@Table(name = "SS_ROLE")
public class Role extends IdEntity {

    private String name;

    //@Column: 将持久字段与其他名称关联 / 微调数据库中列的特征
    // 默认情况, 数据表的列名字和pojo字段的名字相同
    @Column(nullable = false, unique = true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}


最后一个比较重要的类:
@Entity
@Table(name = "SS_USER")
public class User extends IdEntity {
    private String loginName;
    private String plainPassword;
    private String name;
    private String email;
    private String status;
    private Date createTime;

    private List<Role> roleList = Lists.newArrayList(); //有序的关联对象集合

    @Column(nullable = false, unique = true)
    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    //多对多定义
    @ManyToMany
    //中间表定义,表名采用默认命名规则
    //@JoinTable提供程序在映射多对多关联(或在单向的一对多关联中)的拥有方上的实体关联时使用一个连接表。
    //joinColumns属性定义拥有方在连接表上的连接列名    
    //inverseJoinColumns属性定义了被拥有方在连接表上的连接列名
    @JoinTable(name = "SS_USER_ROLE", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
    //Fecth策略定义
    //有三种: join, 子查询以及对每个实体简单的查询
    @Fetch(FetchMode.SUBSELECT)
    //集合按id排序
    @OrderBy("id ASC")
    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }
               
    //@Transient 批注指定实体的非持久字段或属性               
    @Transient
    public String getRoleNames() {
        return ReflectionUtils.convertElementPropertyToString(roleList, "name", ", ");
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

SimpleHibernateDaoTest测试类: 看看能学到什么:
这个测试类使用了一个测试基类SpringTxTestCase, 而这个基类又继承了SpringContextTestCase:
SpringContextTestCase之前已经分析过了, 主要功能:
 * 1.支持Spring 依赖注入功能.
 * 2.支持JUnit Assert功能.

SpringTxTestCase:
Spring的支持数据库访问和依赖注入的JUnit4 集成测试基类.
 *  1.Spring Context IOC support
 *  2.Spring Transaction support
 *  3.Spring JdbcTemplate and util functions
 *  4.JUnit Assert functions

//@Transactional,声明这个类所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。
//需要注意点:
//如果在spring中使用@Transactional的话, 那么在配置文件中必须要声明:
//<!-- 使用annotation定义事务 -->
//<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />   
//来使注解式的声明式事务生效
@Transactional
@TestExecutionListeners(TransactionalTestExecutionListener.class)
public abstract class SpringTxTestCase extends SpringContextTestCase {

    protected DataSource dataSource;

    protected SimpleJdbcTemplate jdbcTemplate;

    protected String sqlScriptEncoding;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
        this.dataSource = dataSource;
    }

    public void setSqlScriptEncoding(String sqlScriptEncoding) {
        this.sqlScriptEncoding = sqlScriptEncoding;
    }

    protected int countRowsInTable(String tableName) {
        return SimpleJdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
    }

    protected int deleteFromTables(String... names) {
        return SimpleJdbcTestUtils.deleteFromTables(this.jdbcTemplate, names);
    }

    protected void runSql(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
        Resource resource = this.applicationContext.getResource(sqlResourcePath);
        SimpleJdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding),
                continueOnError);
    }
}


首先,
@ContextConfiguration(locations = { "/applicationContext-core-test.xml" })
// 自装配, good
@Autowired
private SessionFactory sessionFactory; 
在setUp()中: 
//先删除对象, 然后再执行脚本.
jdbcTemplate.update("drop all objects");
jdbcTemplate.update("runscript from 'core/schema.sql'");

//使用dbunit的类重新包裹datasource
DatabaseDataSourceConnection connection = new DatabaseDataSourceConnection((DataSource) applicationContext
                .getBean("dataSource"));                  
InputStream stream = applicationContext.getResource("classpath:/test-data.xml").getInputStream(); 
//使用dbunit来插入测试数据, 至于数据格式, 参考源码
IDataSet dataSet = new FlatXmlDataSetBuilder().build(stream);
DatabaseOperation.INSERT.execute(connection, dataSet);               

主要看一下如何使用Criterion:
        Criterion c = Restrictions.eq("loginName", DEFAULT_LOGIN_NAME);
        List<User> users = dao.find(c);
        assertEquals(1, users.size());
        assertEquals(DEFAULT_LOGIN_NAME, users.get(0).getLoginName());

        User user = dao.findUnique(c);
        assertEquals(DEFAULT_LOGIN_NAME, user.getLoginName());

        dao.findUnique(c);


看一下如何使用命名绑定查询:
        Map map = new HashMap();
        map.put("ids", new Long[] { 1L, 23L });

        dao.batchExecute("update User u set u.status='disabled' where id in(:ids)", map);
        User u1 = dao.get(1L);
        assertEquals("disabled", u1.getStatus());
        User u3 = dao.get(3L);
        assertEquals("enabled", u3.getStatus());      

再来看一下如何eager获取数据
        String sql = "from User u left join fetch u.roleList order by u.id";

        Query query = dao.createQuery(sql);
        List<User> userList = dao.distinct(query).list();
        assertEquals(6, userList.size());
        assertTrue(Hibernate.isInitialized(userList.get(0).getRoleList()));

        Criteria criteria = dao.createCriteria().setFetchMode("roles", FetchMode.JOIN);
        userList = dao.distinct(criteria).list();
        assertEquals(6, userList.size()); 
        //使用Hibernate自身的函数来判断是否初始化了..
        assertTrue(Hibernate.isInitialized(userList.get(0).getRoleList()));               

posted on 2010-12-11 22:00  菊次郎  阅读(1052)  评论(0)    收藏  举报