Spring的测试框架可以很方便的进行集成测试等,可以摆脱web服务器的束缚就可以完成测试。测试框架可以完成事务管理,依赖注入等功能。
spring4.0.2的API地址:http://docs.spring.io/spring/docs/4.0.2.RELEASE/javadoc-api/
一、测试类实例
下面该例子就可以简单的获取ApplicationContext.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
//@ContextConfiguration(locations= "classpath:/test-servlet-context.xml")
@ContextHierarchy({
// @ContextConfiguration(name="root",classes = AppConfig.class),
// @ContextConfiguration(name="dis",classes = WebConfig.class)
})
public class WebTestextends AbstractJUnit4SpringContextTests{
@Configuration
static class AppConfig {
@Bean
public String foo() {
return "foo";
}
}
@Configuration
static class WebConfig {
@Bean
public String bar() {
return "bar";
}
}
@Test
public void test1(){
Assert.assertNotNull(applicationContext);
}
}
二、测试相关注解
@DirtiesContext 可以在类和方法级别使用。简单来说就是每个测试方法或类在执行开始前重建一个新的上下文,里面生成的数据是脏数据,不缓存到上下文中。
@IfProfileValue 可以在类和方法级别使用。表示只有和name,value值一致时,该类或者方法才会进行测试,否则就略过,代码如“
@IfProfileValue(name= "java.vendor", value = "Sun Microsystems Inc.")表示在
System.getProperty(“java.vendor”)==Sun Microsystems Inc.时该类或者方法才会执行测试
@ProfileValueSourceConfiguration:可以在类级别上使用。表示可以配置上面的IfProfileValue的name和value是从哪里取的。默认实现的是SystemProfileValueSource.class,但可以通过该注解进行自定义,自定义的方法应该继承ProfileValueSource
@Repeat:只能在方法级别上,表示该方法需要重复执行多少次。默认是1
@Rollback:方法级别上,表示执行后是否回滚,true表示会回滚,false表示不回滚
@Time: 表示被注解的方法必须在多长时间内运行完毕,超时将抛出异常,使用如@Timed(millis=10)方式指定,单位为毫秒。注意此处指定的时间是如下方法执行时间之和:测试方法执行时间(或者任何测试方法重复执行时间之和)、@Before和@After注解的测试方法之前和之后执行的方法执行时间。而Junit4中的@Test(timeout=2)指定的超时时间只是测试方法执行时间,不包括任何重复等
ActiveProfiles:类级别的,表示在加载applicationCOntext,应该使用哪个Profile,(在xml和注解中是可以命名profile的),这个注解会加载指定的这些profile。可以通过集成实现
@ContextConfiguration是类级别的,用于确定如何加载和配置的ApplicationContext集成测试
@ContextHierarchy是用来定义一个类级别注释的ApplicationContexts层次集成测试
@ContextHierarchy({
@ContextConfiguration(name ="parent", locations = "/app-config.xml"),
@ContextConfiguration(name ="child", locations ="/user-config.xml")
})
public class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(name ="child", locations ="/test-user-config.xml", inheritLocations=false)
)
@TestExecutionListeners是类级别的,通常,@TestExecutionListeners将与@ContextConfiguration一起使用
@AfterTransaction表示在一个事务提交以后执行这个方法
@BeforeTransaction表示在一个事务开始之前执行这个方法
@TransactionConfiguration表示在一个方法上定义事务的bean和是否提交等
@WebAppConfiguration是让类加载WebApplicationContext而不是ApplicationContext
三、编写测试文件的其他知识
· JUnit 4.5+支持类:提供对Spring TestContext框架与Junit4.5+测试框架的集成:
AbstractJUnit4SpringContextTests:在编写测试类时继承该类,则可以自动使用ApplicationContext等上下文,其实AbstractJUnit4SpringContextTests文件便是通过注解的形式加载了依赖注入,AOP等。
AbstractTransactionalJUnit4SpringContextTests:继承自该类则表示你的测试类已经支持事务,可以操作事务相关的操作。
@RunWith(SpringJUnit4ClassRunner.class):表示将集成Spring Test和Junit 4.5+测试框架,当然也可以自定义RunWith的类,但必须要实现BlockJUnit4ClassRunner方法。
四、测试环境的搭建
测试环境不应该产生脏数据等时因素导致我们需要对测试环境的一些配置进行修改,一个通用的项目应该支持事务、web等,则对应的为了全面的测试,测试环境也应该继承对事务、web等的支持,而上面也说了测试环境是不需要依赖web服务器等外界的。
对JDBC的支持
一种方法是对jdbc的支持如果借助mockito框架则也可以不用真去连接数据等,只需要通过mockito框架就可以进行测试,如下:
@RunWith(MockitoJUnitRunner.class)
public classJdbcTestUtilsTests {
@Mock
private JdbcTemplate jdbcTemplate;
@Test
public void containsDelimiters() {
assertTrue("test with ';' iswrong", !JdbcTestUtils.containsSqlScriptDelimiters("select 1\n select';'", ';'));
assertTrue("test withdelimiter ; is wrong",
JdbcTestUtils.containsSqlScriptDelimiters("select1; select 2", ';'));
assertTrue("test with '\\n'is wrong",
!JdbcTestUtils.containsSqlScriptDelimiters("select1; select '\\n\n';", '\n'));
assertTrue("test withdelimiter \\n is wrong",
JdbcTestUtils.containsSqlScriptDelimiters("select1\n select 2", '\n'));
}
}
第二种方法是真实的连接数据库,但是通过注解来控制不产生脏数据
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:/test-servlet-context.xml")
public classJdbcTest {
@Autowired
private JdbcTemplate jdbcTemplate;
private String testK="nihao";
@BeforeClass
public static void setProfileValue() {
//控制 IfProfileValue的测试方法是不是要进行测试
System.setProperty("testK","nihao1");
}
@Test
public void test1(){
Assert.assertNotNull(jdbcTemplate);
// Assert.assertEquals(jdbcTemplate.queryForInt("select count(*) fromdual"), 1);
// testK="nihao";
}
@Test
@IfProfileValue(name ="testK",value ="nihao")
public void test2(){
Assert.assertNull(jdbcTemplate);
}
}
或
@SuppressWarnings("deprecation")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration(transactionManager= "txMgr", defaultRollback = true)
@Transactional
public classDefaultRollbackFalseTransactionalSpringRunnerTests extendsAbstractTransactionalSpringRunnerTests {
protected static SimpleJdbcTemplatesimpleJdbcTemplate;
@AfterClass
public static void verifyFinalTestData(){
assertEquals("Verifying thefinal number of rows in the person table after all tests.", 2,
countRowsInPersonTable(simpleJdbcTemplate));
}
@Before
public void verifyInitialTestData() {
clearPersonTable(simpleJdbcTemplate);
assertEquals("Adding bob",1, addPerson(simpleJdbcTemplate, BOB));
assertEquals("Verifying theinitial number of rows in the person table.", 1,
countRowsInPersonTable(simpleJdbcTemplate));
}
@Test
public voidmodifyTestDataWithinTransaction() {
assertInTransaction(true);
assertEquals("Deletingbob", 1, deletePerson(simpleJdbcTemplate, BOB));
assertEquals("Addingjane", 1, addPerson(simpleJdbcTemplate, JANE));
assertEquals("Addingsue", 1, addPerson(simpleJdbcTemplate, SUE));
assertEquals("Verifying thenumber of rows in the person table within a transaction.", 2,
countRowsInPersonTable(simpleJdbcTemplate));
}
public static class DatabaseSetup {
@Resource
public voidsetDataSource(DataSource dataSource) {
simpleJdbcTemplate = newSimpleJdbcTemplate(dataSource);
createPersonTable(simpleJdbcTemplate);
}
}
}
对web的支持:
如request的支持可以这样写:
p
ublic classMockHttpServletRequestTests {
private MockHttpServletRequest request =new MockHttpServletRequest();
@Test
public void setContentType() {
String contentType ="test/plain";
request.setContentType(contentType);
assertEquals(contentType,request.getContentType());
assertEquals(contentType,request.getHeader("Content-Type"));
assertNull(request.getCharacterEncoding());
}}
对response的支持可以这样写:
public classMockHttpServletResponseTests {
private MockHttpServletResponse response= new MockHttpServletResponse();
@Test
public void setContentType() {
String contentType ="test/plain";
response.setContentType(contentType);
assertEquals(contentType,response.getContentType());
assertEquals(contentType,response.getHeader("Content-Type"));
assertEquals(WebUtils.DEFAULT_CHARACTER_ENCODING,response.getCharacterEncoding());
}
}
其他对应的Session等均建立的相应的模拟操作
private finalMockHttpSession session = new MockHttpSession();
MockMultipartHttpServletRequestrequest = new MockMultipartHttpServletRequest();
private finalMockServletContext sc = newMockServletContext("org/springframework/mock");