如何测试spring注解controller入controller成功

利用junit对springMVC的Controller进行测试 - 推酷
利用junit对springMVC的Controller进行测试
平时对junit测试service/DAO层已经很熟悉不过了,如果不了解,可以猛戳这里,但是我们要测试controller层,不能总重启服务器吧,(重启tomcat好慢的飘过,别。。。别走啊),那么我们就用junit4模拟请求,测试controller层的方法。
代码1:直接Controller调用方法
import static org.junit.Assert.*;
import java.sql.SQLE
import org.junit.B
import org.junit.T
import org.junit.runner.RunW
import org.springframework.beans.factory.annotation.A
import org.springframework.mock.web.MockHttpServletR
import org.springframework.mock.web.MockHttpServletR
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.SpringJUnit4ClassR
import com.zz.login.web.LoginC
* @author zhzh
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({&classpath*:/beans.xml&,&classpath*:/spring-mvc.xml&})
public class TestController {
// 模拟request,response
private MockHttpServletR
private MockHttpServletR
// 注入loginController
@Autowired
private LoginController loginC
// 执行测试方法之前初始化模拟request,response
public void setUp(){
request = new MockHttpServletRequest();
request.setCharacterEncoding(&UTF-8&);
response = new MockHttpServletResponse();
* @Title:testLogin
* @Description: 测试用户登录
public void testLogin() {
request.setParameter(&userName&, &admin&);
request.setParameter(&password&, &2&);
assertEquals(&login&,loginController.loginIn(request,response)) ;
} catch (Exception e) {
e.printStackTrace();
代码2:调用请求路径
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextS
import org.junit.B
import org.junit.T
import org.junit.runner.RunW
import org.springframework.beans.factory.annotation.A
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.SpringJUnit4ClassR
import org.springframework.test.context.transaction.TransactionC
import org.springframework.test.context.web.WebAppC
import org.springframework.test.web.servlet.MockM
import org.springframework.transaction.annotation.T
import org.springframework.web.context.WebApplicationC
* @author zhzh
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({&classpath*:/beans.xml&,&classpath*:/spring-mvc.xml&})
//当然 你可以声明一个事务管理 每个单元测试都进行事务回滚 无论成功与否
@TransactionConfiguration(defaultRollback = true)
@Transactional
public class TestController2 {
@Autowired
private WebApplicationC
private MockMvc mockM
public void setup() {
this.mockMvc = webAppContextSetup(this.wac).build();
public void testLogin() throws Exception {
mockMvc.perform((post(&/loginTest&).param(&userName&, &admin&).param(&password&, &1&))).andExpect(status().isOk())
.andDo(print());
//有些单元测试你不希望回滚
@Rollback(false)
public void testInsert() throws Exception {
mockMvc.perform((post(&/insertTest&))).andExpect(status().isOk())
.andDo(print());
注意:import static 的spring类
LoginController代码片段
* 登录入口
* @param request
* @param response
@RequestMapping(value = &/loginTest&, method = RequestMethod.POST)
public String loginTest(HttpServletRequest request,HttpServletResponse response){
String account = request.getParameter(&userName&);
String password = request.getParameter(&password&);
if (account.equals(&admin&)&&password.equals(&1&)){
return &index&;
return &login&;
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致二次元同好交流新大陆
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(4194)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Spring MVC防止XSS、SQL注入漏洞攻击方法',
blogAbstract:'XSS漏洞攻击主要分为两种途径:1、URL参数传递2、表单提交参数先说第一种,这种一般都是做为查询条件传递到后端的,来需要保存到数据库中的,处理方法也简单,只要进行转码或过滤就可以了。但是具体方法也有很多,如1)直接对参数进行编码:StringEscapeUtils.escapeHtml(searchWords);在传递可页面显示的时候都可以进行。2)在Spring的控制器中注册属性编辑器下面是一个简单的Spring命令控制器,扩展AbstractCommandController:public class AbstractFrontCommandController extends AbstractCommandController{\tpublic AbstractFrontCommandController(){',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:9,
publishTime:1,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:true,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}14:53 提问
SpringMVC+Spring Controller自动装配失败
使用SpringMVC+Hibernate+Spring做demo练习,但是启动时报错,说是Controller里依赖的LoginService无法自动装配。
我自己写了个Main类做测试,Dao和Service都是可以自动装配的,唯独Controller不可以,网上看了好多类似的问题,但是也没有解决我的问题,希望哪位高手能帮我解答下,谢谢。
启动时报错:
严重: StandardWrapper.Throwable
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'indexController': Injection of autowired nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.ssg.design.bk.service.LoginService com.ssg.design.bk.controller.IndexController.loginS nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.ssg.design.bk.service.LoginService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:672)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:543)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4350)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4659)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
at org.apache.catalina.startup.Embedded.start(Embedded.java:825)
at org.codehaus.mojo.tomcat.AbstractRunMojo.startContainer(AbstractRunMojo.java:558)
at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(AbstractRunMojo.java:255)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.ssg.design.bk.service.LoginService com.ssg.design.bk.controller.IndexController.loginS nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.ssg.design.bk.service.LoginService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 49 more
按赞数排序
你在controller中(注入)service 没有
已经注入了,贴了好几次代码,都没贴成功,结果过了会再登录,发现CSDN把我的账号封了,我去,太Tama牛鼻了。
@Controller
@RequestMapping("/abc")
public class IndexController {
@Autowired
private LoginService loginS
//路径当中的“/”可以省略
@RequestMapping("/index")
public String index() {
System.out.println("aaaaaa");
loginService.getUser();
return "index";
xml 配置有没有配置啊?
@Controller
@RequestMapping("/modelManager")
public class BrandModelController extends InterceptorExceptionController{
@Autowired
private BrandModelService brandModelS
@RequestMapping(value="/addBrandModel", method = RequestMethod.POST)
public String MessageResponse voidBrandModel(){
你照着这个改一下,如果还不成功,就说明配置出问题了
估计应该是注入的问题
67关注|471收录
1038关注|145收录
396关注|937收录
其他相似问题
相关参考资料<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
通过本文,您能够在较短的时间内掌握使用 Spring 单元测试框架测试基于 Spring 的应用程序的方法,这套方法主要涵盖如何使用 Spring 测试注释来进行常见的 Junit4 或者 TestNG 的单元测试,同时支持访问 Spring 的 beanFactory 和进行自动化的事务管理。
, 技术经理
赵才文,长期进行 Java 应用程序架构设计实践和研究,对 Java 新的框架技术有兴趣。
概述单元测试和集成测试在我们的软件开发整个流程中占有举足轻重的地位,一方面,程序员通过编写单元测试来验证自己程序的有效性,另外一方面,管理者通过持续自动的执行单元测试和分析单元测试的覆盖率等来确保软件本身的质量。这里,我们先不谈单元测试本身的重要性,对于目前大多数的基于 Java 的企业应用软件来说,Spring 已经成为了标准配置,一方面它实现了程序之间的低耦合度,另外也通过一些配置减少了企业软件集成的工作量,例如和 Hibernate、Struts 等的集成。那么,有个问题,在普遍使用 Spring 的应用程序中,我们如何去做单元测试?或者说,我们怎么样能高效的在 Spring 生态系统中实现各种单元测试手段?这就是本文章要告诉大家的事情。单元测试目前主要的框架包括 Junit、TestNG,还有些 MOCK 框架,例如 Jmock、Easymock、PowerMock 等,这些都是单元测试的利器,但是当把他们用在 Spring 的开发环境中,还是那么高效么?还好,Spring 提供了单元测试的强大支持,主要特性包括:
支持主流的测试框架 Junit 和 TestNG
支持在测试类中使用依赖注入 Denpendency Injection
支持测试类的自动化事务管理
支持使用各种注释标签,提高开发效率和代码简洁性
Spring 3.1 更是支持在测试类中使用非 XML 配置方法和基于 Profile 的 bean 配置模式通过阅读本文,您能够快速的掌握基于 Spring TestContext 框架的测试方法,并了解基本的实现原理。本文将提供大量测试标签的使用方法,通过这些标签,开发人员能够极大的减少编码工作量。OK,现在让我们开始 Spring 的测试之旅吧!原来我们是怎么做的这里先展示一个基于 Junit 的单元测试,这个单元测试运行在基于 Spring 的应用程序中,需要使用 Spring 的相关配置文件来进行测试。相关类图如下:数据库表假设有一个员工账号表,保存了员工的基本账号信息,表结构如下:
ID:整数类型,唯一标识
NAME:字符串,登录账号
SEX:字符串,性别
AGE:字符串,年龄假设表已经建好,且内容为空。测试工程目录结构和依赖 jar 包在 Eclipse 中,我们可以展开工程目录结构,看到如下图所示的工程目录结构和依赖的 jar 包列表:图 1. 工程目录结构类总体介绍假设我们现在有一个基于 Spring 的应用程序,除了 MVC 层,还包括业务层和数据访问层,业务层有一个类 AccountService,负责处理账号类的业务,其依赖于数据访问层 AccountDao 类,此类提供了基于 Spring Jdbc Template 实现的数据库访问方法,AccountService 和 AccountDao 以及他们之间的依赖关系都是通过 Spring 配置文件进行管理的。现在我们要对 AccountService 类进行测试,在不使用 Spring 测试方法之前,我们需要这样做:此类代表账号的基本信息,提供 getter 和 setter 方法。清单 1. Account.Java
public class Account {
public static final String SEX_MALE = "male";
public static final String SEX_FEMALE = "female";
public String toString() {
return String.format("Account[id=%d,name=%s,age:%d,sex:%s]",id,name,age,sex);
public int getId() {
public void setId(int id) {
public String getName() {
public void setName(String name) {
this.name =
public int getAge() {
public void setAge(int age) {
this.age =
public String getSex() {
public void setSex(String sex) {
this.sex =
public static Account getAccount(int id,String name,int age,String sex) {
Account acct = new Account();
acct.setId(id);
acct.setName(name);
acct.setAge(age);
acct.setSex(sex);
}注意上面的 Account 类有一个 toString() 方法和一个静态的 getAccount 方法,getAccount 方法用于快速获取 Account 测试对象。这个 DAO 我们这里为了简单起见,采用 Spring Jdbc Template 来实现。清单 2. AccountDao.Java package DAO;
import Java.sql.ResultS
import Java.sql.SQLE
import Java.util.HashM
import Java.util.L
import Java.util.M
import org.Springframework.context.ApplicationC
import org.Springframework.context.support.ClassPathXmlApplicationC
import org.Springframework.jdbc.core.RowM
import org.Springframework.jdbc.core.namedparam.NamedParameterJdbcDaoS
import org.Springframework.jdbc.core.simple.ParameterizedRowM
import domain.A
public class AccountDao extends NamedParameterJdbcDaoSupport {
public void saveAccount(Account account) {
String sql = "insert into tbl_account(id,name,age,sex) " +
"values(:id,:name,:age,:sex)";
Map paramMap = new HashMap();
paramMap.put("id", account.getId());
paramMap.put("name", account.getName());
paramMap.put("age", account.getAge());
paramMap.put("sex",account.getSex());
getNamedParameterJdbcTemplate().update(sql, paramMap);
public Account getAccountById(int id) {
String sql = "select id,name,age,sex from tbl_account where id=:id";
Map paramMap = new HashMap();
paramMap.put("id", id);
List&Account& matches = getNamedParameterJdbcTemplate().query(sql,
paramMap,new ParameterizedRowMapper&Account&() {
public Account mapRow(ResultSet rs, int rowNum)
throws SQLException {
Account a = new Account();
a.setId(rs.getInt(1));
a.setName(rs.getString(2));
a.setAge(rs.getInt(3));
a.setSex(rs.getString(4));
return matches.size()&0?matches.get(0):
}AccountDao 定义了几个账号对象的数据库访问方法:
saveAccount:负责把传入的账号对象入库
getAccountById:负责根据 Id 查询账号清单 3. AccountService.Java
import mons.logging.L
import mons.logging.LogF
import org.Springframework.beans.factory.annotation.A
import DAO.AccountD
import domain.A
public class AccountService {
private static final Log log = LogFactory.getLog(AccountService.class);
@Autowired
private AccountDao accountD
public Account getAccountById(int id) {
return accountDao.getAccountById(id);
public void insertIfNotExist(Account account) {
Account acct = accountDao.getAccountById(account.getId());
if(acct==null) {
log.debug("No "+account+" found,would insert it.");
accountDao.saveAccount(account);
}AccountService 包括下列方法:
getAccountById:根据 Id 查询账号信息
insertIfNotExist:根据传入的对象插入数据库其依赖的 DAO 对象 accountDao 是通过 Spring 注释标签 @Autowired 自动注入的。上述几个类的依赖关系是通过 Spring 进行管理的,配置文件如下:清单 4. Spring 配置文件 &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"
xsi:schemaLocation="http://www.Springframework.org/schema/beans
http://www.Springframework.org/schema/beans/Spring-beans-3.0.xsd
http://www.Springframework.org/schema/context
http://www.Springframework.org/schema/context/Spring-context-3.0.xsd "&
&context:annotation-config/&
&bean id="datasource" class="
org.Springframework.jdbc.datasource.DriverManagerDataSource"&
&property name="driverClassName" value="org.hsqldb.jdbcDriver" /&
&property name="url" value="jdbc:hsqldb:hsql://localhost" /&
&property name="username" value="sa" /&
&property name="password" value="" /&
&bean id="initer" init-method="init" class="service.Initializer"&
&bean id="accountDao" depends-on="initer" class="DAO.AccountDao"&
&property name="dataSource" ref="datasource" /&
&bean id="accountService" class="service.AccountService"&
&/beans&注意其中的“&context:annotation-config/&”的作用,这个配置启用了 Spring 对 Annotation 的支持,这样在我们的测试类中 @Autowired 注释才会起作用(如果用了 Spring 测试框架,则不需要这样的配置项,稍后会演示)。另外还有一个 accountDao 依赖的 initer bean, 这个 bean 的作用是加载 log4j 日志环境,不是必须的。另外还有一个要注意的地方,就是 datasource 的定义,由于我们使用的是 Spring Jdbc Template,所以只要定义一个 org.Springframework.jdbc.datasource.DriverManagerDataSource 类型的 datasource 即可。这里我们使用了简单的数据库 HSQL、Single Server 运行模式,通过 JDBC 进行访问。实际测试中,大家可以选择 Oracle 或者 DB2、Mysql 等。好,万事具备,下面我们来用 Junit4 框架测试 accountService 类。代码如下:清单 5. AccountServiceOldTest.Java
import static org.Junit.Assert.assertE
import org.Junit.BeforeC
import org.Junit.T
import org.Springframework.context.ApplicationC
import org.Springframework.context.support.ClassPathXmlApplicationC
import domain.A
public class AccountServiceOldTest {
private static AccountS
@BeforeClass
public static void init() {
ApplicationContext
context = new ClassPathXmlApplicationContext("config/Spring-db-old.xml");
service = (AccountService)context.getBean("accountService");
public void testGetAcccountById() {
Account acct = Account.getAccount(1, "user01", 18, "M");
Account acct2 =
service.insertIfNotExist(acct);
acct2 = service.getAccountById(1);
assertEquals(acct, acct2);
} catch (Exception ex) {
fail(ex.getMessage());
} finally {
service.removeAccount(acct);
}注意上面的 Junit4 注释标签,第一个注释标签 @BeforeClass,用来执行整个测试类需要一次性初始化的环境,这里我们用 Spring 的 ClassPathXmlApplicationContext 从 XML 文件中加载了上面定义的 Spring 配置文件,并从中获得了 accountService 的实例。第二个注释标签 @Test 用来进行实际的测试。测试过程:我们先获取一个 Account 实例对象,然后通过 service bean 插入数据库中,然后通过 getAccountById 方法从数据库再查询这个记录,如果能获取,则判断两者的相等性;如果相同,则表示测试成功。成功后,我们尝试删除这个记录,以利于下一个测试的进行,这里我们用了 try-catch-finally 来保证账号信息会被清除。执行测试:(在 Eclipse 中,右键选择 AccountServiceOldTest 类,点击 Run as Junit test 选项),得到的结果如下:执行测试的结果在 Eclipse 的 Junit 视图中,我们可以看到如下的结果:图 2. 测试的结果对于这种不使用 Spring test 框架进行的单元测试,我们注意到,需要做这些工作:
在测试开始之前,需要手工加载 Spring 的配置文件,并获取需要的 bean 实例
在测试结束的时候,需要手工清空搭建的数据库环境,比如清除您插入或者更新的数据,以保证对下一个测试没有影响另外,在这个测试类中,我们还不能使用 Spring 的依赖注入特性。一切都靠手工编码实现。好,那么我们看看 Spring test 框架能做到什么。首先我们修改一下 Spring 的 XML 配置文件,删除 &context:annotation-config/& 行,其他不变。清单 6. Spring-db1.xml &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-3.2.xsd"&
&bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource"&
&property name="driverClassName" value="org.hsqldb.jdbcDriver" /&
&property name="url" value="jdbc:hsqldb:hsql://localhost" /&
&property name="username" value="sa"/&
&property name="password" value=""/&
&bean id="transactionManager"
class="org.Springframework.jdbc.datasource.DataSourceTransactionManager"&
&property name="dataSource" ref="datasource"&&/property&
&bean id="initer" init-method="init" class="service.Initializer"&
&bean id="accountDao" depends-on="initer" class="DAO.AccountDao"&
&property name="dataSource" ref="datasource"/&
&bean id="accountService" class="service.AccountService"&
&/beans&其中的 transactionManager 是 Spring test 框架用来做事务管理的管理器。清单 7. AccountServiceTest1.Java
import static org.Junit.Assert.assertE
import org.Junit.T
import org.Junit.runner.RunW
import org.Springframework.beans.factory.annotation.A
import org.Springframework.test.context.ContextC
import org.Springframework.test.context.Junit4.SpringJUnit4ClassR
import org.Springframework.transaction.annotation.T
import domain.A
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/config/Spring-db1.xml")
@Transactional
public class AccountServiceTest1 {
@Autowired
private AccountS
public void testGetAcccountById() {
Account acct = Account.getAccount(1, "user01", 18, "M");
service.insertIfNotExist(acct);
Account acct2 = service.getAccountById(1);
assertEquals(acct,acct2);
}对这个类解释一下:
@RunWith 注释标签是 Junit 提供的,用来说明此测试类的运行者,这里用了 SpringJUnit4ClassRunner,这个类是一个针对 Junit 运行环境的自定义扩展,用来标准化在 Spring 环境中 Junit4.5 的测试用例,例如支持的注释标签的标准化
@ContextConfiguration 注释标签是 Spring test context 提供的,用来指定 Spring 配置信息的来源,支持指定 XML 文件位置或者 Spring 配置类名,这里我们指定 classpath 下的 /config/Spring-db1.xml 为配置文件的位置
@Transactional 注释标签是表明此测试类的事务启用,这样所有的测试方案都会自动的 rollback,即您不用自己清除自己所做的任何对数据库的变更了
@Autowired 体现了我们的测试类也是在 Spring 的容器中管理的,他可以获取容器的 bean 的注入,您不用自己手工获取要测试的 bean 实例了
testGetAccountById 是我们的测试用例:注意和上面的 AccountServiceOldTest 中相同的测试方法的对比,这里我们不用再 try-catch-finally 了,事务管理自动运行,当我们执行完成后,所有相关变更会被自动清除执行结果在 Eclipse 的 Junit 视图中,我们可以看到如下的结果:图 3. 执行结果小结如果您希望在 Spring 环境中进行单元测试,那么可以做如下配置:
继续使用 Junit4 测试框架,包括其 @Test 注释标签和相关的类和方法的定义,这些都不用变
您需要通过 @RunWith(SpringJUnit4ClassRunner.class) 来启动 Spring 对测试类的支持
您需要通过 @ContextConfiguration 注释标签来指定 Spring 配置文件或者配置类的位置
您需要通过 @Transactional 来启用自动的事务管理
您可以使用 @Autowired 自动织入 Spring 的 bean 用来测试另外您不再需要:
手工加载 Spring 的配置文件
手工清理数据库的每次变更
手工获取 application context 然后获取 bean 实例Spring 测试注释标签我们已经看到利用 Spring test framework 来进行基于 Junit4 的单元测试是多么的简单,下面我们来看一下前面遇到的各种注释标签的一些可选用法。@ContextConfiguration 和 @Configuration 的使用刚才已经介绍过,可以输入 Spring xml 文件的位置,Spring test framework 会自动加载 XML 文件,得到 application context,当然也可以使用 Spring 3.0 新提供的特性 @Configuration,这个注释标签允许您用 Java 语言来定义 bean 实例,举个例子:现在我们将前面定义的 Spring-db1.xml 进行修改,我们希望其中的三个 bean:initer、accountDao、accountService 通过配置类来定义,而不是 XML,则我们需要定义如下配置类:注意:如果您想使用 @Configuration,请在 classpath 中加入 cglib 的 jar 包(cglib-nodep-2.2.3.jar),否则会报错。清单 8. SpringDb2Config.Java
import org.Springframework.beans.factory.annotation.A
import org.Springframework.context.annotation.B
import org.Springframework.context.annotation.C
import org.Springframework.jdbc.datasource.DriverManagerDataS
import service.AccountS
import service.I
import DAO.AccountD
@Configuration
public class SpringDb2Config {
private @Autowired DriverManagerDataS
public Initializer initer() {
return new Initializer();
public AccountDao accountDao() {
AccountDao DAO = new AccountDao();
DAO.setDataSource(datasource);
return DAO;
public AccountService accountService() {
return new AccountService();
}注意上面的注释标签:
@Configuration:表明这个类是一个 Spring 配置类,提供 Spring 的 bean 定义,实际效果等同于 XML 配置方法
@Bean:表明这个方法是一个 bean 的定义,缺省情况下,方法名称就是 bean 的 Id
@Autowired:这个 datasource 采用自动注入的方式获取注意,我们采用的是 XML+config bean 的方式进行配置,这种方式比较符合实际项目的情况。相关的 Spring 配置文件也要做变化,如下清单所示:清单 9. Spring-db2.xml &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"
xsi:schemaLocation="http://www.Springframework.org/schema/beans
http://www.Springframework.org/schema/beans/Spring-beans-3.0.xsd
http://www.Springframework.org/schema/context
http://www.Springframework.org/schema/context/Spring-context-3.0.xsd"&
&context:annotation-config/&
&bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource"&
&property name="driverClassName" value="org.hsqldb.jdbcDriver" /&
&property name="url" value="jdbc:hsqldb:hsql://localhost" /&
&property name="username" value="sa"/&
&property name="password" value=""/&
&bean id="transactionManager"
class="org.Springframework.jdbc.datasource.DataSourceTransactionManager"&
&property name="dataSource" ref="datasource"&&/property&
&bean class="config.SpringDb2Config"/&
&/beans&注意里面的 context 命名空间的定义,如代码中黑体字所示。另外还必须有 &context:annotaiton-config/& 的定义,这个定义允许采用注释标签的方式来控制 Spring 的容器,最后我们看到 beans 已经没有 initer、accountDao 和 accountService 这些 bean 的定义,取而代之的是一个 SpringDb2Config bean 的定义,注意这个 bean 没有名称,因为不需要被引用。现在有了这些配置,我们的测试类只要稍稍修改一下,即可实现加载配置类的效果,如下: @ContextConfiguration("/config/Spring-db2.xml")通过上面的配置,测试用例就可以实现加载 Spring 配置类,运行结果也是成功的 green bar。@DirtiesContext缺省情况下,Spring 测试框架一旦加载 applicationContext 后,将一直缓存,不会改变,但是,由于 Spring 允许在运行期修改 applicationContext 的定义,例如在运行期获取 applicationContext,然后调用 registerSingleton 方法来动态的注册新的 bean,这样的情况下,如果我们还使用 Spring 测试框架的被修改过 applicationContext,则会带来测试问题,我们必须能够在运行期重新加载 applicationContext,这个时候,我们可以在测试类或者方法上注释:@DirtiesContext,作用如下:
如果定义在类上(缺省),则在此测试类运行完成后,重新加载 applicationContext
如果定义在方法上,即表示测试方法运行完成后,重新加载 applicationContext@TransactionConfiguration 和 @Rollback缺省情况下,Spring 测试框架将事务管理委托到名为 transactionManager 的 bean 上,如果您的事务管理器不是这个名字,那需要指定 transactionManager 属性名称,还可以指定 defaultRollback 属性,缺省为 true,即所有的方法都 rollback,您可以指定为 false,这样,在一些需要 rollback 的方法,指定注释标签 @Rollback(true)即可。对 Junit4 的注释标签支持看了上面 Spring 测试框架的注释标签,我们来看看一些常见的基于 Junit4 的注释标签在 Spring 测试环境中的使用方法。@Test(expected=...)此注释标签的含义是,这是一个测试,期待一个异常的发生,期待的异常通过 xxx.class 标识。例如,我们修改 AccountService.Java 的 insertIfNotExist 方法,对于传入的参数如果为空,则抛出 IllegalArgumentException,如下: public void insertIfNotExist(Account account) {
if(account==null)
throw new IllegalArgumentException("account is null");
Account acct = accountDao.getAccountById(account.getId());
if(acct==null) {
log.debug("No "+account+" found,would insert it.");
accountDao.saveAccount(account);
}然后,在测试类中增加一个测试异常的方法,如下: @Test(expected=IllegalArgumentException.class)
public void testInsertException() {
service.insertIfNotExist(null);
}运行结果是 green bar。@Test(timeout=...)可以给测试方法指定超时时间(毫秒级别),当测试方法的执行时间超过此值,则失败。比如在 AccountService 中增加如下方法: public void doSomeHugeJob() {
Thread.sleep(2*1000);
} catch (InterruptedException e) {
}上述方法模拟任务执行时间 2 秒,则测试方法如下: @Test(timeout=3000)
public void testHugeJob() {
service.doSomeHugeJob();
}上述测试方法期待 service.doSomeHugeJob 方法能在 3 秒内结束,执行测试结果是 green bar。@Repeat通过 @Repeat,您可以轻松的多次执行测试用例,而不用自己写 for 循环,使用方法: @Repeat(3)
@Test(expected=IllegalArgumentException.class)
public void testInsertException() {
service.insertIfNotExist(null);
}这样,testInsertException 就能被执行 3 次。在测试类中基于 profile 加载测试 bean从 Spring 3.2 以后,Spring 开始支持使用 @ActiveProfiles 来指定测试类加载的配置包,比如您的配置文件只有一个,但是需要兼容生产环境的配置和单元测试的配置,那么您可以使用 profile 的方式来定义 beans,如下:清单 10. Spring-db.xml &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-3.2.xsd"&
&beans profile="test"&
&bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource"&
&property name="driverClassName" value="org.hsqldb.jdbcDriver" /&
&property name="url" value="jdbc:hsqldb:hsql://localhost" /&
&property name="username" value="sa"/&
&property name="password" value=""/&
&beans profile="production"&
&bean id="datasource"
class="org.Springframework.jdbc.datasource.DriverManagerDataSource"&
&property name="driverClassName" value="org.hsqldb.jdbcDriver" /&
&property name="url" value="jdbc:hsqldb:hsql://localhost/prod" /&
&property name="username" value="sa"/&
&property name="password" value=""/&
&beans profile="test,production"&
&bean id="transactionManager"
class="org.Springframework.jdbc.datasource.DataSourceTransactionManager"&
&property name="dataSource" ref="datasource"&&/property&
&bean id="initer" init-method="init" class="service.Initializer"&
&bean id="accountDao" depends-on="initer" class="DAO.AccountDao"&
&property name="dataSource" ref="datasource"/&
&bean id="accountService" class="service.AccountService"&
&bean id="envSetter" class="EnvSetter"/&
&/beans&上面的定义,我们看到:
在 XML 头中我们引用了 Spring 3.2 的 beans 定义,因为只有 Spring 3.2+ 才支持基于 profile 的定义
在 &beans& 根节点下可以嵌套 &beans& 定义,要指定 profile 属性,这个配置中,我们定义了两个 datasource,一个属于 test profile,一个输入 production profile,这样,我们就能在测试程序中加载 test profile,不影响 production 数据库了
在下面定义了一些属于两个 profile 的 beans,即 &beans profile=”test,production”& 这样方便重用一些 bean 的定义,因为这些 bean 在两个 profile 中都是一样的清单 11. AccountServiceTest.Java @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/config/Spring-db.xml")
@Transactional
@ActiveProfiles("test")
public class AccountServiceTest {
}注意上面的 @ActiveProfiles,可以指定一个或者多个 profile,这样我们的测试类就仅仅加载这些名字的 profile 中定义的 bean 实例。对 TestNG 的支持Spring 2.5 以后,就开始支持 TestNG 了,支持的方法包括:
将您的 TestNG 测试类继承 Spring 的测试父类:AbstractTransactionalTestNGSpringContextTests 或者 AbstractTestNGSpringContextTests,这样您的 TestNG 测试类内部就可以访问 applicationContext 成员变量了
不继承 Spring 父类,在测试类上使用 @TestExecutionListeners 注释标签,可以引入的监听器包括
DependencyInjectionTestExecutionListener:使得测试类拥有依赖注入特性
DirtiesContextTestExecutionListener:使得测试类拥有更新 applicationContext 能力
TransactionalTestExecutionListener:使得测试类拥有自动的事务管理能力这里我们演示一下如何使用 Spring 提供的 TestNG 父类来进行测试。清单 12. AccountServiceTestNGTest.Java
import static org.Junit.Assert.assertE
import org.Springframework.beans.factory.annotation.A
import org.Springframework.test.context.ActiveP
import org.Springframework.test.context.ContextC
import org.Springframework.test.context.testng.
AbstractTransactionalTestNGSpringContextT
import org.Springframework.transaction.annotation.T
import service.AccountS
import domain.A
@ContextConfiguration("/config/Spring-db.xml")
@Transactional
@ActiveProfiles("test")
public class AccountServiceTestNGTest extends
AbstractTransactionalTestNGSpringContextTests {
@Autowired
private AccountS
@org.testng.annotations.Test
public void testGetAcccountById() {
Account acct = Account.getAccount(1, "user01", 18, "M");
service.insertIfNotExist(acct);
Account acct2 = service.getAccountById(1);
assertEquals(acct,acct2);
}执行测试,我们将看到测试成功。图 4. 测试成功搜索数据库对应的表,我们看到里面没有数据,说明自动事务起作用了。基本原理Spring test framework 主要位于 org.Springframework.test.context 包中,主要包括下面几个类:图 5. Spring 测试框架类图()
TestContextManager:主要的入口类,提供 TestContext 实例的管理,负责根据各种事件来通知测试监听器
TestContext:实体类,提供访问 Spring applicatoin context 的能力,并负责缓存 applicationContext
TestExecutionListener:测试监听器,提供依赖注入、applicationContext 缓存和事务管理等能力
ContextLoader:负责根据配置加载 Spring 的 bean 定义,以构建 applicationContext 实例对象
SmartContextLoader:Spring 3.1 引入的新加载方法,支持按照 profile 加载Spring 通过 AOP hook 了测试类的实例创建、beforeClass、before、after、afterClass 等事件入口,执行顺序主要如下:图 6. Spring 测试框架执行序列图()
测试执行者开始执行测试类,这个时候 Spring 获取消息,自动创建 TestContextManager 实例
TestContextManager 会创建 TestContext,以记录当前测试的上下文信息,TestContext 则通过 ContextLoader 来获取 Spring ApplicationContext 实例
当测试执行者开始执行测试类的 BeforeClass、Before、After、AfterClass 的时候,TestContextManager 将截获事件,发通知给对应的 TestExecutionListener总结根据上面的例子和介绍,我们可以看到,Spring 测试框架的主要特点如下:
完美的支持了 Junit4(提供特别的 SpringJunit4ClassRunner),比较好的支持了 TestNG
在支持原有单元测试能力的基础上,通过各种监听器,支持了测试类的依赖注入、对 Spring applicationContext 的访问以及事务管理能力,为使用 Spring 架构的应用程序的测试带来了极大的便利性
Spring 3.1 引入的基于 profile 的加载能力使得测试环境和正式环境可以在一个 XML 定义中完美的结合总之,如果您的程序中使用了 Spring,且对用 Junit 或者 testNG 来对他们进行单元测试感到力不从心,可以考虑使用 Spring test framework,它将使您的应用程序的质量上一个新的台阶。
参考资料 :介绍 Spring 3.0 所有 API 功能和用法的参考文档。
:全面介绍 Spring 框架功能和用法的参考手册。
:主要介绍了如何使用 JUnit4 提供的各种功能开展有效的单元测试,并通过一个实例演示了如何使用 Ant 执行自动化的单元测试。
:这里有数百篇关于 Java 编程各个方面的文章。
加入 :查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=931591ArticleTitle=使用 Spring 进行单元测试publish-date=}

我要回帖

更多关于 spring 单元测试 注入 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信