在进行dao的集成测试时候,数据清理,察看数据都是比较麻烦的事情,使用Spring-mock.jar可以帮助我们简化着一个过程。我举一个简单的例子,说明一下如何使用spring-mock。
首先是po, hbm.xml, dao, daoimpl没什么好说的:
- Customer.java :
- package rst.spring.mock;
- import java.io.Serializable;
- /** @author Hibernate CodeGenerator */
- public class Customer implements Serializable {
- /** identifier field */
- private Long id;
- /** nullable persistent field */
- private String name;
- /** full constructor */
- public Customer(String name) {
- this.name = name;
- }
- /** default constructor */
- public Customer() {
- }
- public Long getId() {
- return this.id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- public String getName() {
- return this.name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- Customer.hbm.xml :
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
- <hibernate-mapping package="rst.spring.mock">
- <class name="Customer" table="customer">
- <id name="id" column="id" type="long" unsaved-value="null">
- <generator class="identity"/>
- </id>
- <property name="name" column="name" type="string"/>
- </class>
- </hibernate-mapping>
- CustomerDAO :
- /*
- * Created on 2005-3-25
- */
- package rst.spring.mock;
- import org.springframework.dao.DataAccessException;
- /**
- * @author rst
- *
- */
- public interface CustomerDAO {
- public void add(Customer customer) throws DataAccessException;
- }
- CustomerDAOImpl :
- package rst.spring.mock;
- import org.springframework.dao.DataAccessException;
- import org.springframework.orm.hibernate.support.HibernateDaoSupport;
- /**
- * Class description.
- *
- * @author rst
- */
- public class CustomerDAOHibernateImpl extends HibernateDaoSupport implements CustomerDAO{
- public void add(Customer customer) throws DataAccessException{
- this.getHibernateTemplate().save(customer);
- }
- }
然后测试的基类SpringDAOTestCase继承自AbstractTransactionalDataSourceSpringContextTests,目前只有一个指定测试用xml文件位置的逻辑。
- package rst.spring.mock;
- import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
- /**
- * Class description.
- *
- * @author rst
- */
- public abstract class SpringDAOTestCase extends AbstractTransactionalDataSourceSpringContextTests {
- protected String[] getConfigLocations() {
- return new String[] { "test.xml" };
- }
- }
接着是我们真正测试的类CustomerDAOTest.java:
- package rst.spring.mock;
- /**
- * Class description.
- *
- * @author rst
- */
- public class CustomerDaoTest extends SpringDAOTestCase {
- private CustomerDAOHibernateImpl customerDAO;
- protected void onSetUpInTransaction() throws Exception {
- super.onSetUpInTransaction();
- //this.setPopulateProtectedVariables(true);
- customerDAO = (CustomerDAOHibernateImpl) this.applicationContext.getBean("customerDAO");
- }
- protected void onTearDownInTransaction() {
- customerDAO = null;
- }
- public void testInsert() {
- Customer customer = new Customer();
- customer.setName("javaeye");
- customerDAO.add(customer);
- String name = (String) jdbcTemplate.queryForObject("select name from customer where id=?", new Object[]{customer.getId()}, String.class);
- assertEquals(customer.getName(), name);
- }
- }
最后看看配置文件test.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
- <!--
- - Application context definition for Petclinic on Hibernate.
- -->
- <beans>
- <!-- ========================= RESOURCE DEFINITIONS ========================= -->
- <!-- Configurer that replaces ${...} placeholders with values from a properties file -->
- <!-- (in this case, JDBC-related settings for the dataSource definition below) -->
- <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="location"><value>classpath:jdbc.properties</value></property>
- </bean>
- <!-- Local DataSource that works in any environment -->
- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
- <property name="url"><value>${jdbc.url}</value></property>
- <property name="username"><value>${jdbc.username}</value></property>
- <property name="password"><value>${jdbc.password}</value></property>
- </bean>
- <!-- Hibernate SessionFactory -->
- <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
- <property name="dataSource"><ref local="dataSource"/></property>
- <property name="mappingResources">
- <value>rst/spring/mock/Customer.hbm.xml</value>
- </property>
- <property name="hibernateProperties">
- <props>
- <prop key="hibernate.dialect">${hibernate.dialect}</prop>
- <prop key="hibernate.show_sql">true</prop>
- </props>
- </property>
- </bean>
- <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
- <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
- <property name="sessionFactory"><ref local="sessionFactory"/></property>
- </bean>
- <bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate">
- <property name="sessionFactory"><ref local="sessionFactory"/></property>
- </bean>
- <bean id="customerDAO" class="rst.spring.mock.CustomerDAOHibernateImpl">
- <property name="hibernateTemplate"><ref local="hibernateTemplate"/></property>
- </bean>
- </beans>
这个文件很简单,不要忘记transactionManager的配置,Test类会自动装配的。
运行之后,就可以看到应有的结果,并且数据库中不会有数据污染。这个过程主要是开始一个transaction,然后开始你的test方法,执行dao操作,执行sql查询验证结果,最后无论成功失败rollback transaction。
也许我孤陋寡闻,我是最近才发现的。
1.Spring Mock。
Mock主要用于测试Controller(or Action),Controller等同于一个Servlet,要测试它里面的方法,必须至少传入HttpRequest和HttpResponse
两个类的实例作为参数参数,直接new 一个HttpRequest是豪无意义的,必须使用mock。
Spring的mock那个包就提供了HttpRequest、HttpResponse、HttpSession的mock,测试controller时就可以直接new一个对象!
完整的测试代码Demo如下:
- package org.ggyy.web.test;
- import junit.framework.TestCase;
- import java.io.File;
- import java.io.IOException;
- import java.util.List;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import junit.framework.TestCase;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.ggyy.bo.Cat;
- import org.ggyy.web.ctrl.CatController;
- import org.springframework.mock.web.MockHttpServletRequest;
- import org.springframework.mock.web.MockHttpServletResponse;
- import org.springframework.mock.web.MockServletContext;
- import org.springframework.web.context.support.XmlWebApplicationContext;
- import org.springframework.web.servlet.ModelAndView;
- public class ListCatTest extends TestCase {
- private static Log log = LogFactory.getLog(ListCatTest.class);
- private XmlWebApplicationContext ctx;
- public void setUp() {
- String[] paths = {
- "file:/d:/eclipse/workspace/cat/WebRoot/WEB-INF/applicationContext.xml",
- "file:/d:/eclipse/workspace/cat/WebRoot/WEB-INF/pet-servlet.xml" };
- ctx = new XmlWebApplicationContext();
- ctx.setConfigLocations(paths);
- ctx.setServletContext(new MockServletContext(""));
- ctx.refresh();
- }
- public void testGetUsers() throws Exception {
- MockHttpServletRequest request = new MockHttpServletRequest();
- MockHttpServletResponse response = new MockHttpServletResponse();
- request.addParameter("parentId", "2");
- CatController c = (CatController) ctx.getBean("catController");
- ModelAndView mav = c.listCatHandler(request, response);
- Map m = mav.getModel();
- }
- }
<script type="text/javascript">render_code();</script>
catController是MultiActionController,listCatHandler是其中的一个action。这里使用了绝对路径: "file:/d:/eclipse/workspace/cat/WebRoot/WEB-INF/pet-servlet.xml"。相对路径好像调试不出来。
2.基于Spring容器管理事务的测试--可以我表述不是很准确^_^
对于service层和dao的测试一般都是在实际的数据库中进行测试的,假如你不想使得测试对原来数据库造成任何影响,可以使用这种测试策略。整个测试是在Spring管理的事务中进行的,测试完毕,可以选择提交事务,也可以不提交事务,也就是说测试完后对原来的数据库的记录不造成任何影响。
这种测试策略最为诱人的还是:当测试不通过,也就是抛出异常时,事务可以自动回滚的!我以前测试前要清空数据库,测试不通过时测试用例后面的delete方法不能运行,我不得不手工的删除这些数据。
对于测试用例所依赖的bean,声明该bean的一个setter方法spring就可以自动的注入一个实例--很有意思,和ejb3.0类似。
我从pdf文档考下来一个demo供大家观赏:
- public abstract class AbstractClinicTests extends AbstractTransactionalDataSourceSpringContextTests {
- protected Clinic clinic;
- public void setClinic(Clinic clinic) {
- this.clinic = clinic;
- }
- public void testGetVets() {
- Collection vets = this.clinic.getVets();
- assertEquals("JDBC query must show the same number of vets",
- jdbcTemplate.queryForInt("SELECT COUNT(0) FROM VETS"),
- vets.size());
- Vet v1 = (Vet) EntityUtils.getById(vets, Vet.class, 2);
- assertEquals("Leary", v1.getLastName());
- assertEquals(1, v1.getNrOfSpecialties());
- assertEquals("radiology", ((Specialty) v1.getSpecialties().get(0)).getName());
- Vet v2 = (Vet) EntityUtils.getById(vets, Vet.class, 3);
- assertEquals("Douglas", v2.getLastName());
- assertEquals(2, v2.getNrOfSpecialties());
- assertEquals("dentistry", ((Specialty) v2.getSpecialties().get(0)).getName());
- assertEquals("surgery", ((Specialty) v2.getSpecialties().get(1)).getName());
- }
<script type="text/javascript">render_code();</script>
上面的clinic实例是自动注入的,