Spring JUnit 测试用例失败

Posted

技术标签:

【中文标题】Spring JUnit 测试用例失败【英文标题】:Spring JUnit test case failed 【发布时间】:2013-07-12 15:56:49 【问题描述】:

我尝试为 ProductDAO 类运行 JUnit 测试用例,它是 Spring Web 应用程序的一部分。但它仍然失败。我不确定我的 test-context.xml 文件。

我在路径src/test/java 中有ProductDAOImplTest 类,并提到堆栈跟踪中的ProductController 类保存在路径src/main/java 中。

ProductDAOImplTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/test/resources/test-context.xml")
@Transactional
public class ProductDAOImplTest 

    @Autowired
    private ProductDAO productDAO;

    @Test
    public void testAdd() 
        fail("Not yet implemented");
    

    @Test
    public void testEdit() 
        fail("Not yet implemented");
    

    @Test
    public void testGetAll() 
        fail("Not yet implemented");
    

    @Test
    public void testGet() 
        fail("Not yet implemented");
    

    @Test
    public void testRemove() 
        fail("Not yet implemented");
    

    @Test
    public void testGetByPage() 
        fail("Not yet implemented");
    


test-context.xml

<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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:lang="http://www.springframework.org/schema/lang" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="com.myapp" />
    <context:annotation-config />

    <!-- JDBC -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="file:src/main/webapp/WEB-INF/jdbc.properties" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" p:driverClassName="$jdbc.driverClassName"
        p:url="$jdbc.databaseurl" p:username="$jdbc.username" p:password="$jdbc.password" />

    <!-- Hibernate -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">$jdbc.dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>



</beans>

堆栈跟踪

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:103)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:73)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.servlet.http.HttpServletRequest com.myapp.controller.ProductController.request; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.http.HttpServletRequest] 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:288)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1120)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:607)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.servlet.http.HttpServletRequest com.myapp.controller.ProductController.request; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.http.HttpServletRequest] 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:514)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
    ... 40 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.http.HttpServletRequest] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:818)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:730)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 42 more

【问题讨论】:

我认为您的file:src/test/resources/test-context.xml 应该可以作为çlasspath:test-context.xml 使用 【参考方案1】:

错误在堆栈跟踪中

无法自动装配字段:私有 javax.servlet.http.HttpServletRequest com.myapp.controller.ProductController.request; ... [javax.servlet.http.HttpServletRequest] 类型没有匹配的 bean

问题在于简单的 Spring 测试使用裸 Spring 应用程序上下文运行,没有 Spring Web 应用程序上下文功能。所以 HttpServletRequests、Servlet Context 等特性在这种情况下是不可用的。

尝试在测试类中添加@WebAppConfiguration注解。

请注意,此选项仅在 Spring 3.2+ 版本中可用。

在旧版本的 Spring 中,您需要发明一些东西以使该接口实现在应用程序上下文中可用(最直接的方法是将 MockServletContext 类和/或 HttpServletRequest 接口公开为用于测试的 bean)。

另外请注意,使用会话范围 bean 的代码通常是特定于控制器的,属于 web application context,我相信最好的做法是使用 @WebAppConfiguration 在单独的测试中测试控制器,但不要进行测试属于简单应用程序上下文中的根应用程序上下文的普通旧 bean 和服务,即没有@WebAppConfiguration

要明确ProductDAO属于根应用上下文,ProductController属于web应用上下文,所以它们的定义应该放在不同的xml文件中。 ProductDAO 测试应该只指向 根应用上下文的 xml 并且不包含 @WebAppConfiguration

ProductController 测试应该指向根应用上下文和 web 应用上下文 xml 文件(参见 How to Setup web application context in Spring MVC test 示例)并使用 @WebAppConfiguration 进行注释

【讨论】:

【参考方案2】:

您目前正在做的事情很好,尽管我会说它更像是集成测试而不是单元测试。

我的主要建议是只调出您对被测组件绝对需要的系统部分,例如ProductDAO

您似乎已经在某种程度上这样做了,但也可能有助于将您的 component-scan 范围缩小到您保留 DAO 的位置​​,例如:

<context:component-scan base-package="com.myapp.dao" />

这将避免使用更高级别的组件,例如控制器和服务。

然后您可以将上下文重命名为 test-dao-context.xml 并在所有其他 DAO 测试中使用它。

当您对服务进行集成测试时,您可以拥有一个 test-service-context.xml 上下文,该上下文导入 test-dao-context.xml,允许您从服务测试到数据库,而无需过多重复 bean 定义。

【讨论】:

谢谢,组件扫描包的改变解决了这个问题。建议的方式是spring框架的标准还是你的伎俩? @misco 我不能说它是否是标准的,但到目前为止它对我来说效果很好。很高兴您解决了问题。【参考方案3】:

您的单元测试设置让我(非常)紧张。您正在尝试引导所有内容,包括休眠、事务管理器和组件扫描。您几乎需要自己的容器来执行此操作。

通常,一个好的单元测试只关注每个测试类的一个类,它的所有依赖项都使用 Mockito 等库进行模拟

【讨论】:

我是 Spring 测试的初学者,那么应该如何看待正确的单元测试设置? 问题中的测试不是单元测试,但它没有任何问题 - 它是一个容器内集成测试,用于测试 Spring 上下文配置和 bean(JUnit 只是一个工具在这种情况下运行测试)。【参考方案4】:

我同意 gerrytan 和 Jonathan 所写的内容。我还建议查看Springockito 以便于模拟。

【讨论】:

以上是关于Spring JUnit 测试用例失败的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 测试用例中“失败”的实际用途是啥?

junit4.9测试用例 spring测试用例 Assert 注解

运行 Junit 测试用例时“加载 ApplicationContext 失败”

如何为 Spring 托管 bean 编写 Junit 测试用例?

spring-integration-file 的 junit 测试用例

Junit测试用例在STS中成功运行,但无法通过Jmeter运行。