Beginning Spring学习笔记——第7章 使用Spring进行测试驱动开发

来源:互联网 发布:网站编程常见单词 编辑:程序博客网 时间:2024/05/22 07:57

配置和缓存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。

@Configurationpublic 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 {}

然后创建测试类:

@Configurationpublic 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")@WebAppConfigurationpublic 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);    }}

然后创建一个简单的控制器。

@Controllerpublic 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请求并测试返回值是否符合预期。测试通过。
然后创建表单提交控制类。

@Controllerpublic 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加入了输入变量作为提交表单的内容,然后用同样的方法测试返回值。测试通过。
最后创建异常处理类。

@Controllerpublic 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"));    }}

测试通过。

阅读全文
0 0