配置和缓存ApplicationContext
测试中使用基于XML和Java的上下文配置
首先创建maven项目,依赖配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lonelyquantum.springbeginning.wileybookch4</groupId>
<artifactId>JDBCTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>JDBCTest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
在src/main/java下创建com.wiley.beginningspring.ch7包,创建第二章创建的一系列类和接口。
Account类:
public class Account {
private long id;
private String ownerName;
private double balance;
private Date accessTime;
private boolean locked;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public Date getAccessTime() {
return accessTime;
}
public void setAccessTime(Date accessTime) {
this.accessTime = accessTime;
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) `
this.locked = locked;
}
}
AccountDao接口和其实现类AccountDaoInMemoryImpl:
public interface AccountDao {
public void insert(Account account);
public void update(Account account);
public void update(List<Account> accounts);
public void delete(long accountId);
public Account find(long accountId);
public List<Account> find(List<Long> accountIds);
public List<Account> find(String ownerName);
public List<Account> find(boolean locked);
}
public class AccountDaoInMemoryImpl implements AccountDao {
private Map<Long,Account> accountsMap = new HashMap<>();
public void setAccountsMap(Map<Long, Account> accountsMap) {
this.accountsMap = accountsMap;
}
{
Account account1 = new Account();
account1.setId(1L);
account1.setOwnerName("John");
account1.setBalance(10.0);
Account account2 = new Account();
account2.setId(2L);
account2.setOwnerName("Mary");
account2.setBalance(20.0);
accountsMap.put(account1.getId(), account1);
accountsMap.put(account2.getId(), account2);
}
@Override
public void insert(Account account) {
accountsMap.put(account.getId(), account);
}
@Override
public void update(Account account) {
accountsMap.put(account.getId(), account);
}
@Override
public void update(List<Account> accounts) {
for(Account account:accounts) {
update(account);
}
}
@Override
public void delete(long accountId) {
accountsMap.remove(accountId);
}
@Override
public Account find(long accountId) {
return accountsMap.get(accountId);
}
@Override
public List<Account> find(List<Long> accountIds) {
List<Account> accounts = new ArrayList<>();
for(Long id:accountIds) {
accounts.add(accountsMap.get(id));
}
return accounts;
}
@Override
public List<Account> find(String ownerName) {
List<Account> accounts = new ArrayList<>();
for(Account account:accountsMap.values()) {
if(ownerName.equals(account.getOwnerName())) {
accounts.add(account);
}
}
return accounts;
}
@Override
public List<Account> find(boolean locked) {
List<Account> accounts = new ArrayList<>();
for(Account account:accountsMap.values()) {
if(locked == account.isLocked()) {
accounts.add(account);
}
}
return accounts;
}
}
AccountService接口和其实现类AccountServiceImpl。
public interface AccountService {
public void transferMoney(long sourceAccountId, long targetAccountId, double amount);
public void depositMoney(long accountId, double amount) throws Exception;
public Account getAccount(long accountId);
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transferMoney(long sourceAccountId, long targetAccountId, double amount) {
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
@Override
public void depositMoney(long accountId, double amount) throws Exception {
Account account = accountDao.find(accountId);
account.setBalance(account.getBalance() + amount);
accountDao.update(account);
}
@Override
public Account getAccount(long accountId) {
return accountDao.find(accountId);
}
}
采用XML配置时,在src/main/resource下创建applicationContext.xml Bean配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountService" class="com.wiley.beginningspring.ch7.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<bean id="accountDao" class="com.wiley.beginningspring.ch7.AccountDaoInMemoryImpl"/>
</beans>
此时在src/test/java文件夹中创建com.wiley.beginningspring.ch7包并创建测试XML配置的测试类AccountIntegrationTests。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class AccountIntegrationTests {
@Autowired
private AccountService accountService;
@Test
public void accountServiceShouldBeInjected() {
Assert.assertNotNull(accountService);
}
}
作为JUnit测试运行通过,说明配置成功。
此外,还可以使用Java文件配置,此时不需要创建以上XML文件,而是在类包内创建配置类Ch7Configuration。
@Configuration
public class Ch7Configuration {
@Bean
public AccountService accountService() {
AccountServiceImpl bean = new AccountServiceImpl();
bean.setAccountDao(accountDao());
return bean;
}
@Bean
public AccountDao accountDao() {
AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
//depedencies of accountDao bean will be injected here...
return bean;
}
}
此时在测试包中创建的文件变为:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Ch7Configuration.class})
public class AccountIntegrationTestsWithJavaConfig {
@Autowired
private AccountService accountService;
@Test
public void accountServiceShouldBeInjected() {
Assert.assertNotNull(accountService);
}
}
当然,也可以两种配置一起使用,此时测试类变为:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Ch7Configuration.class,Config.class})
public class AccountIntegrationTestsWithMixedConfig {
@Configuration
@ImportResource("classpath:/applicationContext.xml")
static class Config {
}
@Autowired
private AccountService accountService;
@Test
public void accountServiceShouldBeInjected() {
Assert.assertNotNull(accountService);
}
}
此外还可以使用ApplicationContextInitializer来配置上下文,这时,先在一个TestInitializer类中完成配置文件的导入和上下文的预初始化:
public class TestInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext applicationContext) {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(applicationContext);
reader.loadBeanDefinitions("classpath:/applicationContext.xml");
}
}
然后再利用该上下文进行测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers={TestInitializer.class})
public class AccountIntegrationTestsWithInitializer {
@Autowired
private AccountService accountService;
@Test
public void accountServiceShouldBeInjected() {
Assert.assertNotNull(accountService);
}
}
以上测试目录结构为:
继承上下文配置
继承于中间类或者基础类的类可以继承测试相关配置。
先展示目录结构:
首先创建如下两个类:
public class Bar {
}
public class Foo {
}
然后创建基础类的配置文件baseContext.xml。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="com.wiley.beginningspring.ch7.Foo"/>
</beans>
接着创建子配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bar" class="com.wiley.beginningspring.ch7.Bar"/>
</beans>
然后创建基础测试类和孩子测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/baseContext.xml")
public class BaseTest {
@Autowired
protected Foo foo;
}
@ContextConfiguration("classpath:/subContext.xml")
public class ChildTest extends BaseTest {
@Autowired
private Bar bar;
@Test
public void dependenciesShouldBeAvailable() {
Assert.assertNotNull(foo);
Assert.assertNotNull(bar);
}
}
运行孩子测试类通过,说明孩子测试类继承了基础测试类的配置文件。
ApplicationContext缓存
如果多个测试类指定了完全相同的XML位置和配置类,那么Spring TestContext Framework将只创建一次ApplicatoinContext实例,并在运行这些测试类之间共享该实例。
缓存被保存再一个静态变量中。
注入测试夹具的依赖项
项目目录结构如下:
首先创建如下两个类:
public class Bar {
}
public class Foo {
}
然后创建测试类:
@Configuration
public class Ch7ConfigurationForDependencyInjection {
@Bean
public Foo foo1() {
return new Foo();
}
@Bean
public Foo foo2() {
return new Foo();
}
@Bean
public Bar bar1() {
return new Bar();
}
}
最后创建测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=Ch7ConfigurationForDependencyInjection.class)
public class DependencyInjectionTests {
@Autowired
@Qualifier("foo1")
private Foo foo1;
@Resource
private Foo foo2;
@Resource
private Bar bar;
@Test
public void testInjections() {
Assert.assertNotNull(foo1);
Assert.assertNotNull(foo2);
Assert.assertNotNull(bar);
}
}
运行通过测试,可见配置类中的依赖被成功注入到测试类中。
在测试中使用事务管理
在测试方法或者测试类级别使用@Transactional注解即可作为事务测试,通常测试后会进行回滚操作,如果不想回滚而是想提交事务,则需要另外设置不回滚。
@Test
@Transactional
@Rollback(false)
public void transactionalTestMethod(){
}
此外,使用ORM框架,如Hibernate或JPA时,要在测试方法结束时刷新当前Hibernate Session或者JPA EntityManager,否则由于测试结束后进行回滚,其上累积的持久化操作不会提交,SQL操作不会执行,导致无法检查由数据库交互失败的错误。
测试Web应用程序
此时需要支持web的依赖文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lonelyquantum.springbeginning.wileybookch4</groupId>
<artifactId>JDBCTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>JDBCTest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.10.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<javax.servlet.version>3.1.0</javax.servlet.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet.version}</version>
</dependency>
</dependencies>
</project>
测试的目录结构为:
首先创建空的XML配置文件applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
然后创建如下测类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
@WebAppConfiguration
public class WebApplicationTests {
@Autowired
private WebApplicationContext applicationContext;
@Autowired
private MockServletContext servletContext;
@Autowired
private MockHttpServletRequest httpServletRequest;
@Autowired
private MockHttpServletResponse httpServletResponse;
@Test
public void testWebApp() {
Assert.assertNotNull(applicationContext);
Assert.assertNotNull(servletContext);
Assert.assertNotNull(httpServletRequest);
Assert.assertNotNull(httpServletResponse);
}
}
注意到类上使用了@WebAppConfiguration表示为Web项目。运行测试成功通过。
测试Request和Session作用域的Bean
依赖文件不变,目录结构如下:
首先创建如下三个类:
public class LoginAction {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class UserPreferences {
private String theme;
public String getTheme() {
return theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
}
public class UserService {
private LoginAction loginAction;
private UserPreferences userPreferences;
public LoginAction getLoginAction() {
return loginAction;
}
public void setLoginAction(LoginAction loginAction) {
this.loginAction = loginAction;
}
public UserPreferences getUserPreferences() {
return userPreferences;
}
public void setUserPreferences(UserPreferences userPreferences) {
this.userPreferences = userPreferences;
}
}
用户操作和用户偏好为两个域类,而用户服务类中设置这两个域类实例。
在配置文件applicationContext.xml中用SpEL定义了他们的Bean并注入了依赖:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="loginAction" class="com.wiley.beginningspring.ch7.LoginAction" scope="request">
<property name="username" value="#{request.getParameter('username')}"/>
<property name="password" value="#{request.getParameter('password')}"/>
<aop:scoped-proxy/>
</bean>
<bean id="userPreferences" class="com.wiley.beginningspring.ch7.UserPreferences" scope="session">
<property name="theme" value="#{session.getAttribute('theme')}"/>
<aop:scoped-proxy/>
</bean>
<bean id="userService" class="com.wiley.beginningspring.ch7.UserService">
<property name="loginAction" ref="loginAction"/>
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
然后子测试类中验证了注入的依赖:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:/applicationContext.xml")
public class ScopedBeanTests {
@Autowired
private UserService userService;
@Autowired
private MockHttpServletRequest httpServletRequest;
@Autowired
private MockHttpSession httpSession;
@Test
public void testScopedBeans() {
httpServletRequest.setParameter("username", "jdoe");
httpServletRequest.setParameter("password", "secret");
httpSession.setAttribute("theme", "blue");
Assert.assertEquals("jdoe",userService.getLoginAction().getUsername());
Assert.assertEquals("secret", userService.getLoginAction().getPassword());
Assert.assertEquals("blue", httpSession.getAttribute("theme"));
}
}
测试Spring MVC项目
该项目需要的maven依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.service.service</groupId>
<artifactId>SpringAOPTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Web -->
<jsp.version>2.3.1</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>3.1.0</servlet.version>
<!-- Spring -->
<spring-framework.version>4.3.10.RELEASE</spring-framework.version>
<!-- Hibernate / JPA -->
<hibernate.version>5.2.10.Final</hibernate.version>
<!-- Logging -->
<logback.version>1.2.3</logback.version>
<slf4j.version>1.7.25</slf4j.version>
<!-- Test -->
<junit.version>4.12</junit.version>
<!-- AspectJ -->
<aspectj.version>1.8.10</aspectj.version>
<!-- JSON Evaluation -->
<jackson.version>2.9.0</jackson.version>
<!-- Validator -->
<hibernate-validator.version>6.0.2.Final</hibernate-validator.version>
<javax-validation.version>2.0.0.Final</javax-validation.version>
</properties>
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Other Web dependencies -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Test Artifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- JSON Evaluation -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax-validation.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-all -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish/javax.el -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
<dependencies>
</project>
项目目录结构如下:
首先创建项目所需要的User和simpleUser域类。
public class User {
@Size(min=3, max=20)
String username;
@Email
String email;
@CreditCardNumber
String ccNumber;
@Pattern(regexp = "^[a-zA-Z]\\w{3,14}$")
String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getCcNumber() {
return ccNumber;
}
public void setCcNumber(String ccNumber) {
this.ccNumber = ccNumber;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class SimpleUser {
private String name;
private String lastName;
public SimpleUser() {
}
public SimpleUser(String name, String lastName) {
this.name = name;
this.lastName = lastName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
然后创造查找失败抛出的异常类。
public class UserNotFoundException extends Exception {
public UserNotFoundException(String name) {
super("User not found with name: " + name);
}
}
然后创建一个简单的控制器。
@Controller
public class HelloReaderController {
@RequestMapping(value = "/hello")
public ModelAndView sayHello() {
ModelAndView mv = new ModelAndView();
mv.addObject("message", "Hello Reader!");
mv.setViewName("helloReader");
return mv;
}
}
为方便测试,创建一个加载上下文的基类,之后的所有测试类都继承该类从而继承上下文配置。
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/springmvc-servlet.xml")
public abstract class BaseControllerTests {
}
其中上下文配置文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.lonelyquantum.wileybookch7" />
<context:annotation-config />
<mvc:annotation-driven validator="validator" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans>
创建测试简单控制器的测试类。
public class HelloReaderControllerTests extends BaseControllerTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void helloReaderControllerWorksOk() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(model().attribute("message", "Hello Reader!"))
.andExpect(view().name("helloReader"));
}
@Test
public void helloReaderControllerWorksOkWithAnUnmappedUrl() throws Exception {
mockMvc.perform(post("/helloMyLove"))
.andExpect(status().isNotFound());
}
}
测试中使用MockMvc类来进行控制器测试。可以看见该类可以发送http请求并测试返回值是否符合预期。测试通过。
然后创建表单提交控制类。
@Controller
public class UserController {
@RequestMapping(value = "/result")
public ModelAndView processUser(@Valid User user, BindingResult result) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("u", user);
if (result.hasErrors()) {
modelAndView.setViewName("userForm");
}
else {
modelAndView.setViewName("userResult");
}
return modelAndView;
}
}
和它的测试类。
public class UserControllerTests extends BaseControllerTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void formSubmittedSuccessfully() throws Exception {
this.mockMvc.perform(
post("/result")
.param("username", "johndoe")
.param("email", "john@doe.com")
.param("ccNumber", "5245771326014172")
.param("password", "TestR0ck"))
.andExpect(status().isOk())
.andExpect(view().name("userResult"))
.andExpect(model().hasNoErrors())
.andExpect(model().attribute("u", hasProperty("username", is("johndoe"))))
.andExpect(model().attribute("u", hasProperty("email", is("john@doe.com"))))
.andExpect(model().attribute("u", hasProperty("ccNumber", is("5245771326014172"))))
.andExpect(model().attribute("u", hasProperty("password", is("TestR0ck"))));
}
@Test
public void formSubmittedSuccessfullyButContainsValidationErrors() throws Exception {
this.mockMvc.perform(
post("/result")
.param("username", "ok"))
.andExpect(status().isOk())
.andExpect(view().name("userForm"))
.andExpect(model().hasErrors());
}
}
该测试类中通过param加入了输入变量作为提交表单的内容,然后用同样的方法测试返回值。测试通过。
最后创建异常处理类。
@Controller
public class User2Controller {
private Map<String, SimpleUser> users = new HashMap<String, SimpleUser>();
@PostConstruct
public void setup() {
users.put("mert", new SimpleUser("Mert", "Caliskan"));
users.put("kenan", new SimpleUser("Kenan", "Sevindik"));
}
@RequestMapping(value = "/findUser")
public ModelAndView processUser(String name) throws Exception {
ModelAndView modelAndView = new ModelAndView();
SimpleUser user = users.get(name);
if (user == null) {
throw new UserNotFoundException(name);
}
modelAndView.addObject("u", user);
modelAndView.setViewName("userResult");
return modelAndView;
}
@ExceptionHandler(UserNotFoundException.class)
public ModelAndView handleException(UserNotFoundException e) {
ModelAndView modelAndView = new ModelAndView("errorUser");
modelAndView.addObject("errorMessage", e.getMessage());
return modelAndView;
}
}
和它的测试类。
public class User2ControllerTests extends BaseControllerTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void userNotFoundExceptionHandledSuccessfully() throws Exception {
this.mockMvc.perform(get("/findUser").param("name", "johndoe"))
.andExpect(status().isOk())
.andExpect(view().name("errorUser"))
.andExpect(model().attribute("errorMessage", "User not found with name: johndoe"));
}
}
测试通过。