首页 > 代码库 > Spring MVC的单元测试和集成测试(不使用mock)

Spring MVC的单元测试和集成测试(不使用mock)

文章要点:

1. 为Controller编写测试,不需要应用服务器环境

2. 为Service编写测试,不需要应用服务器环境


Spring为我们提供了一个测试套件Spring-test,与JUnit结合,可以在运行测试时启动IoC容器测试Service,数据库,也可以在脱离web容器的环境下模拟http请求测试Controller,甚是给力。

测试Controller

当我们编写完一个Controller后,常规的测试方式就是部署运行应用,然后观察运行效果。如果想要编写测试,也要用Mock对象模拟请求参数,模拟HttpSession,特别麻烦。现在spring-test为我们提供了一个更加优雅的测试方式。例如,要测试下面的Controller:
@RequestMapping(value = http://www.mamicode.com/{"/", "/shop"}, method = RequestMethod.GET)>
其对应的测试代码为:
@Test
	public void testHome() throws Exception {
		MockMvc mock = MockMvcBuilders.standaloneSetup(homeController).build();

		mock.perform(get("/")).andExpect(status().isOk())
				.andExpect(forwardedUrl("index"));
		
		
	}

在测试代码中,我们向 HomeController 发送GET请求 "/", 然后判断(andExpect()方法)HTTP状态码是否为200,再判断是否跳转到了"index"页面。如果有任何一环出错,测试就会失败。
我们也可以传送请求参数:
mock.perform(get("/").param("username", "password"))
			.andExpect(status().isOk());

注意,之所以能够直接调用get()这样的方法是因为使用了 static import:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


完整的测试代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class HomeControllerTest {
	@Autowired
	private HomeController homeController;
	

	@Test
	public void testHome() throws Exception {
		MockMvc mock = MockMvcBuilders.standaloneSetup(homeController).build();

		mock.perform(get("/")).andExpect(status().isOk())
				.andExpect(forwardedUrl("index"));

		
		
	}

测试Service

Service方法一般会联系到数据操作,所以单纯JUnit难以进行测试。但Spring-test可以轻松应对。JUnit通过@RunWith注解将控制权交给Spring,从而让Spring启动IoC容器:
@RunWith(SpringJUnit4ClassRunner.class)

这样依赖注入就可以使用了(@Autowired注解)。但在使用之前,要配置bean,如果涉及数据库操作,还要配置LocalContainerEntityManagerFactoryBean ,就像我们在web开发中的配置一样。数据库配置bean如下:
/**
 * 数据库测试配置类
 * @author whf
 *
 */
@Configuration
public class DatabaseConfigure {
	@Autowired
	private DataSource dataSource;

	@Autowired
	private EntityManagerFactory entityManagerFactory;


	@Bean
	public FactoryBean<EntityManagerFactory> entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();

		emfb.setDataSource(this.dataSource);
		emfb.setJpaVendorAdapter(jpaVendorAdapter());
		emfb.setPackagesToScan("你的package");
		
		Properties prop = new Properties();
		prop.put("hibernate.hbm2ddl.auto", "create-drop");
		prop.put("hibernate.show_sql", "true");
		prop.put("hibernate.format_sql", "true");
		prop.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
		
		emfb.setJpaProperties(prop);
		
		return emfb;
	}

	@Bean
	public JpaVendorAdapter jpaVendorAdapter() {
		return new HibernateJpaVendorAdapter();
	}

	@Bean
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager txManager = new JpaTransactionManager();
		txManager.setEntityManagerFactory(this.entityManagerFactory);
		txManager.setDataSource(this.dataSource);
		return txManager;
	}

	@Bean
	public DataSource dataSource() {
		EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
		builder.setType(EmbeddedDatabaseType.H2);
		return builder.build();
	}
}

这样一来我们就可以注入EntityManager了。一个完整的测试代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DatabaseConfigure.class,
		DefaultAccountService.class})
@TransactionConfiguration
@Transactional(readOnly = true)
public class AccountServiceTest {
	@Autowired
	private AccountService acService;
	
	@PersistenceContext
	private EntityManager em;
	
	private Member m;
	
	@Before
	@Transactional(readOnly = false)
	public void initAccountData() {
		m = new Member();
		m.setUsername("bruce");
		m.setPassword("3d4f2bf07dc1be38b20cd6e46949a1071f9d0e3d");
		
		em.persist(m);
	}
	
	@After
	@Transactional(readOnly = false)
	public void cleanDatabase() {
		m = em.merge(m);
		em.remove(m);
	}

	
	@Test
	public void testFindByUsername() {
		Member m = acService.findMember("bruce");
		Assert.assertNotNull(m);
		Assert.assertEquals("bruce", m.getUsername());
		Assert.assertEquals("3d4f2bf07dc1be38b20cd6e46949a1071f9d0e3d", m.getPassword());
	}

有了spring-test,省去了服务器启动和应用部署的时间,从而大大提高了开发效率,同时也减少了BUG的产生。
要运行上述测试代码,需要的maven依赖如下:
<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
		</dependency>

<!-- JDBC连接池 -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.4.182</version>
			<scope>test</scope>
		</dependency>


祝大家编码愉快~

Spring MVC的单元测试和集成测试(不使用mock)