Seam 使用心得(一)测试先行

最近开始关注Seam,正值Seam2.0发布之际我也下载来试用,还是准备先做一个用户注册的例子。由于一直喜欢TDD所以先从测试用例开始写起吧。
首先使用seam-gen生成一个项目目录,然后再进行必要的配置。
1)配置数据源,我使用的hsqldb内存数据库,这样比较方便
在myApp-test-ds.xml这样进行配置
<datasources>
   <local-tx-datasource>
        <jndi-name>myAppTestDatasource</jndi-name>
        <connection-url>jdbc:hsqldb:.</connection-url>
        <driver-class>org.hsqldb.jdbcDriver</driver-class>
        <user-name>sa</user-name>
        <password></password>
    </local-tx-datasource>
</datasources>


persistence-test.xml中的配置
<persistence-unit name="myApp" transaction-type="JTA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:/myAppTestDatasource</jta-data-source>
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
         <property name="hibernate.show_sql" value="true"/>
         <property name="hibernate.format_sql" value="false"/>
         <property name="hibernate.cache.use_second_level_cache" value="false"/>
         <property name="jboss.entity.manager.factory.jndi.name" value="java:/myAppEntityManagerFactory"/>
      </properties>
</persistence-unit>


这样测试数据源的配置就完成了,当然还要把hsqldb.jar放在测试的classpath中。

2)配置测试用例
在测试文件夹中创建一个用于配置测试用例类的xml,我的就是在test/org/qpt/test/user里面建立一个RegisterTest.xml,内容如下:
<suite name="Register Tests" verbose="2" parallel="false">
    <test name="Register Test">
	<!--配置jndi数据源,用于数据库测试-->
        <parameter name="datasourceJndiName" value="java:/myAppTestDatasource"/>
        <classes>
            <class name="org.qpt.test.user.RegisterTest" />
        </classes>
    </test>
</suite>


3) 检查你的build.xml中test任务是否正确的拷贝了测试所有需要用到的jar包和xml文件,我的build.xml是用seam-gen自动生成的,有些文件例如dataset.xml模拟数据库的标准数据的文件就没有被自动拷贝到相应的目录导致运行测试就报异常,我为此郁闷了好几天,网上一直找不到答案,结果静下心仔细研究了一下才发现build.xml的问题-_-!,所以我在build.xml加入了如下任务:
<copy todir="${test.dir}">
            <fileset dir="${src.test.dir}">
                <exclude name="**/*.java" />
            </fileset>
</copy>
<copy tofile="${test.dir}/myApp-ds.xml"
              file="${basedir}/resources/myApp-test-ds.xml"
              overwrite="true"/>


4)编写测试用例
一般来说用户注册要调用一个register()的业务方法,所以我的无状态session bean接口如下:

package org.qpt.facade;
import javax.ejb.Local;

@Local
public interface Register {

    /**
     * 注册一个新用户
     * @return 状态码有5种情况:1,注册成功(SUCCESS)
     * 2,两次输入的密码不同(PWD_NOT_EQ_IN_TWICE)
     * 3,两次输入的email不同(EMAIL_NOT_EQ_IN_TWICE)
     * 4,用户名已经在数据库里面存在(EXISTS_NAME)
     * 5,email已经在数据库里面存在(EXISTS_EMAIL)
     */
    public String register();
}


返回的状态码可以用enum来实现,如:
public enum RegState {
        EXISTS_EMAIL, EXISTS_NAME, PWD_NOT_EQ_IN_TWICE, EMAIL_NOT_EQ_IN_TWICE, SUCCESS
}

现在就可以在org.qpt.test.user.RegisterTest类里面开始编写测试用例了,但是由于使用了ejb所以现在要用到嵌入式jboss,要把jboss-embedded-all.jar放到测试的classpath中。( 注意:根据官方文档的说明,嵌入式jboss只能在jdk1.5下面使用,如果使用的jdk1.6将报异常,但是经过搜索官方网站已经发现了解决方案,那就是要下载一个新的org.jboss.metadata.spi.signature.Signature类重新编译后覆盖原始的,这个类的源文件在最后附上)

下面的代码是测试代码的一部分,完整的代码应该验证register方法返回的5种状态 :
public class RegisterTest extends DBUnitSeamTest {// 1
@Override
    protected void prepareDBUnitOperations() {
        beforeTestOperations.add(new DataSetOperation("org/qpt/test/user/RegisterDataSet.xml")); // 2
    }
	......
@Test
    public void testCorrectCase() throws Exception { //3
        new FacesRequest() {

            @Override
            protected void updateModelValues() {
                setValue("#{user.name}", "qwerty");
                setValue("#{user.pwd}", "123456");
                setValue("#{user.confirmPwd}", "123456");
                setValue("#{user.email}", "[email protected]");
                setValue("#{user.confirmEmail}", "[email protected]");
            }

            @Override
            protected void processValidations() {
                validateValue("#{user.name}", "qwerty");
                validateValue("#{user.pwd}", "123456");
                validateValue("#{user.confirmPwd}", "123456");
                validateValue("#{user.email}", "[email protected]");
                validateValue("#{user.confirmEmail}", "[email protected]");
                assert !isValidationFailure();
            }

            @Override
            protected void invokeApplication() {
                assert invokeMethod("#{register.register}").equals("SUCCESS");
            }
        }.run();
    }
  @Test
    public void testPwdIllegaCase() throws Exception { 
        new FacesRequest() {

            @Override
            protected void updateModelValues() {
                setValue("#{user.name}", "qwerty");
                setValue("#{user.pwd}", "654321");
                setValue("#{user.confirmPwd}", "123456");
                setValue("#{user.email}", "[email protected]");
                setValue("#{user.confirmEmail}", "[email protected]");
            }

            @Override
            protected void invokeApplication() {
                assert invokeMethod("#{register.register}").equals("PWD_NOT_EQ_IN_TWICE");
            }
        }.run();
    }
	......

@Test
    public void testNameExistsCase() throws Exception {
        new FacesRequest() {

            @Override
            protected void updateModelValues() {
                setValue("#{user.name}", "hypercube");
                setValue("#{user.pwd}", "654321");
                setValue("#{user.confirmPwd}", "654321");
                setValue("#{user.email}", "[email protected]");
                setValue("#{user.confirmEmail}", "[email protected]");
            }

            @Override
            protected void invokeApplication() {
                assert invokeMethod("#{register.register}").equals("EXISTS_NAME");
            }
        }.run();
    }

	......

}

1,因为用户注册肯定要操作数据库,所以我继承了DBUnitSeamTest,这样就可以方便的使用dbunit和hsqldb内存数据库进行测试了。
2,继承DBUnitSeamTest就要实现他的一个抽象方法protected void prepareDBUnitOperations(),主要是使用标准数据初始化数据库。RegisterDataSet.xml里面就是定义了表结构和标准数据,我的users表内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <users name="hypercube" pwd="123456" email="[email protected]" />
</dataset>

如果你被有指定其他的操作,DataSetOperation默认使用DatabaseOperation.CLEAN_INSERT作为一个构造参数。这个例子首先清空定义在BaseData.xml中所有的表然后插入所有声明在BaseData.xml中的行在@test方法每次调用之前。
3,这个方法模拟了JSF请求的生命周期包括初始化模型的值,验证模型属性的合法性(配合hibernate验证),调用register()方法,然后通过assert验证方法是否正确的执行。

你可能感兴趣的:(xml,Hibernate,jboss,HSQLDB,seam)