当前位置: 首页 > 面试题库 >

Struts2; 为StrutsSpringTestCase JUnit测试保持会话打开

屈宏爽
2023-03-14
问题内容

我的项目架构是带有Spring集成和JPA / Hibernate的Struts2。StrutsSpringTestCase基类用于JUnit集成测试。

在正常情况下,web.xml中的以下配置从每个请求的开始到结束都保持单个会话打开:

<filter>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

结果,所有延迟加载都可以在所有服务中正常工作。例如:

@Override
public Person find(int id) {
    Person person = personDao.find(id);

    // Take care of lazy loading before detaching the object for
    // the view layer...
    person.getGender().getCode();

    // Detach the object so that it can be used for data transfer
    // (as a DTO) without causing JPA issues and errors...
    getEntityManager().detach(person);

    return person;
}

现在…当我尝试运行集成测试时会出现问题,这些测试独立于web.xml中的OpenEntityManagerInViewFilter配置。发生的事情是,由于没有一个会话从每个请求的开始到结束都保持打开状态,因此诸如“
person.getGender()。getCode()”之类的延迟加载语句不再起作用,而我得到了“无法初始化”代理-没有会话”错误。

我知道的一种解决方案是在出现延迟加载问题的服务方法上强制使用@Transactional批注,这将导致从方法调用的开始到结束都打开一个会话。我测试了它并解决了问题:

@Transactional
@Override
public Person find(int id) {
    Person person = personDao.find(id);

    // Take care of lazy loading before detaching the object for
    // the view layer...
    person.getGender().getCode();

    // Detach the object so that it can be used for data transfer
    // (as a DTO) without causing JPA issues and errors...
    getEntityManager().detach(person);

    return person;
}

但是,由于该方法在正常情况下不需要事务,因此这可能是过大的。我想知道是否还有另一种不需要在服务端妥协的解决方案。

我可以添加一些测试类(扩展了StrutsSpringTestCase)来保持会话打开吗?还是在Spring或JUnit方面有一个优雅的配置解决方案?

这是我的Spring配置文件-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"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
        default-dependency-check="all"
        default-lazy-init="false"
        default-autowire="byName">


    <!-- *************** MAIN CONFIGURATION SECTION *************** -->

    <!-- Bean post-processor for JPA annotations. -->
    <!-- Make the Spring container act as a JPA container and inject an EnitityManager from
         the EntityManagerFactory. -->
    <bean   class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
            autowire="no"
            dependency-check="none" />


    <!-- ** Data Source Configuration ** -->
    <bean   id="dataSource"
            class="com.mchange.v2.c3p0.ComboPooledDataSource"
            destroy-method="close"
            autowire="no"
            dependency-check="none">
        <!-- Database configuration: -->
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost/**********" />
        <property name="user" value="**********" />
        <property name="password" value="**********" />
        <!-- C3P0 pooling properties configuration: -->
        <property name="acquireIncrement" value="4" />
        <property name="initialPoolSize" value="4" />
        <property name="minPoolSize" value="4" />
        <property name="maxPoolSize" value="20" />
        <property name="maxIdleTime" value="600" />
        <property name="maxConnectionAge" value="1800" />
    </bean>


    <!-- ** JPA Vendor Selection ** -->
    <bean   id="jpaVendorAdapter"
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
            autowire="no"
            dependency-check="none" />


    <!-- ** JPA Vendor and Entity Manager Configuration ** -->
    <bean   id="entityManagerFactory"
            class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
            autowire="no"
            dependency-check="none">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>

                <!-- Have the JPA vendor manage the database schema: -->
                <prop key="hibernate.hbm2ddl.auto">create</prop>

                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="hibernate.max_fetch_depth">4</prop>
                <prop key="hibernate.jdbc.batch_size">1000</prop>

                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">false</prop>
            </props>
        </property>
    </bean>


    <!-- ** Transaction Manager Configuration ** -->
    <bean   id="transactionManager"
            class="org.springframework.orm.jpa.JpaTransactionManager"
            autowire="no"
            dependency-check="none">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>


    <!-- ** Transaction Annotation Configuration; classes/functions with @Transactional will
            get a framework transaction. ** -->
    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- **** DETAILED SERVICE BEAN CONFIGURATION WAS TAKEN OUT TO SHORTEN THE FILE **** -->

</beans>

我将不胜感激任何指针。

编辑:

为了让事情有点更直观,当有问题的服务方法遇到延迟加载和业务方法,下面的测试生成异常 使用@Transactional注解,但是当服务方法就好作品
使用@Transactional注解。

public class ActionTest extends CustomActionTestBase {

    public ActionTest() {
        super("/web/someAction"); // the action to test
    }

    @Override
    public void testHelperActionLoggedIn() throws Exception {
        procApplyContinualSessionForAdmin(); // the numerous steps to get logged in

        procExecuteAction(
                helpGetPrimaryActionURI(),  // use the action URI set by the constructor above
                helpPrepareActionParams( )  // no parameters are passed to this action
            );

        procConfirmOutcome(ActionSupport.SUCCESS,0,0,0,false);
    }

}

注意:CustomActionTestBase扩展了StrutsSpringTestCase(这又扩展了一些JUnit的东西)。由于需要大量的测试用例自定义/自动化,因此我需要CustomActionTestBase。

编辑:

我还尝试将@Transactional添加到“ testHelperActionLoggedIn()”测试方法本身,但这并没有改变结果。

编辑:

此外,我尝试通过使用@ RunWith,@ ContextConfiguration和@Test进行注释来使事情更特定于Spring(如Aleksandr
M的指示)。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class ActionTest extends CustomActionTestBase {

    public ActionTest() {
        super("/web/someAction"); // the action to test
    }

    @Test
    @Override
    public void testHelperActionLoggedIn() throws Exception {
        procApplyContinualSessionForAdmin(); // the numerous steps to get logged in

        procExecuteAction(
                helpGetPrimaryActionURI(),  // use the action URI set by the constructor above
                helpPrepareActionParams( )  // no parameters are passed to this action
            );

        procConfirmOutcome(ActionSupport.SUCCESS,0,0,0,false);
    }

}

它导致出现在JUnit故障跟踪中的异常-出于任何原因,控制台中都没有异常输出。例外详情:

java.lang.NullPointerException
at org.apache.struts2.StrutsTestCase.getActionMapping(StrutsTestCase.java:196)
at org.apache.struts2.StrutsTestCase.getActionMapping(StrutsTestCase.java:206)
at com.mycompany.utils.test.CustomActionTestBase.examineActionMapping(CustomActionTestBase.java:402)
at com.mycompany.utils.test.CustomActionTestBase.procExecuteAction(CustomActionTestBase.java:158)
at com.mycompany.utils.test.CustomActionTestBase.execLoginActionForAdmin(CustomActionTestBase.java:505)
at com.mycompany.utils.test.CustomActionTestBase.procApplyContinualSessionForAdmin(CustomActionTestBase.java:106)
at com.mycompany.actions.web.ActionTest.testHelperActionLoggedIn(ActionTest.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
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:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
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)

似乎在获取动作映射时遇到了麻烦,这是以前没有的。


问题答案:

您可以将@Transactional注解放在测试方法上,并且需要使用spring运行测试才能找到@Transactional注解。要在Struts2测试中使用JUnit4,您需要扩展StrutsSpringJUnit4TestCase。因此您的测试类应如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class ActionTest extends StrutsSpringJUnit4TestCase {
  @Transactional
  @Test
  public void testHelperActionLoggedIn() throws Exception {
    // ...
  }
}

注意:如果需要获取ActionProxy,可以通过调用getActionProxymethod获得。您可能需要为其创建新的会话映射,然后才能致电execute

ActionProxy actionProxy = getActionProxy("/action");
Map<String, Object> sessionMap = new HashMap<String, Object>();
actionProxy.getInvocation().getInvocationContext().setSession(sessionMap);
actionProxy.execute();

但是,如果您不需要引用,ActionProxy则可以使用executeAction方法执行操作,而无需创建新的会话映射。

executeAction("/action");


 类似资料:
  • 我正在构建一个反向代理服务器,它将授权http请求并将其传递给一些内部API。 我根据DB中的条目授权传入请求。 这个项目中的性能是优先的——反向代理服务器不应该增加太多响应时间。 简而言之: 我正在使用来查询数据库。我在Spring配置中的init期间打开Hibernate会话: 然后我将会话注入DAO层,并在read方法中使用它: 正如您所看到的,我并不是在每次DAO调用中都关闭/打开会话。我

  • 我正在尝试验证Spring启动后端并获取一些数据 身份验证工作正常。以下是响应标题: 然后我试着从Spring启动中获取项目。这是我的HTTP GET的标题: 它不使用会话cookie,因此会抛出一个错误,说没有标识。 如何使用angular 4.3解决此问题?

  • 我正在Struts2中创建一个应用程序。 在这方面,我面临一个会话管理方面的挑战。 如果用户第一次在IE浏览器中打开登录页面,那么我希望显示页面,用户将在该页面中输入用户名和密码。成功提供登录凭据后,我将使用在会话范围中存储一个属性,例如data 这里我使用的是tomcat持久性JDBC基本存储。我能够存储会话id和它的数据在数据库中。 场景:由于某种网络问题或服务器问题,我们强行重启服务器。此时

  • 我面临交易错误,例如: org . hibernate . lazyinitializationexception:无法延迟初始化角色集合:app。Parent.children,无法初始化代理-没有会话 这里在。 但我对方法有: 1个事务在上创建工厂并给实体管理器 1个事务在上创建实体 我希望能够测试我的Hibernate会话何时处于活动状态,这样我就可以检查它何时丢失以及丢失的原因。 谢谢!

  • 任何web应用程序的核心内容之一是控制和维持用户交互状态的机制。这通常被认为是会话管理,定义为一系列用于管理用户和web应用系统交互状态的措施。这广泛覆盖了从用户如何认证到他们登出时发生的任何事情。 HTTP是一个无状态的协议,意味着web服务器在相应用户请求时不需要联系其他请求。但甚至有时是简单的应用程序逻辑也可能需要通过一个“会话”来关联用户发送的多个请求。这便需要第三方解决方案的介入,通过现

  • 问题内容: 我正在尝试使用Selenium Web驱动程序和node js自动执行几个页面。我可以登录,但是登录后我想使用由Web驱动程序启动的同一会话,以便可以在受会话保护的页面上进行自动测试。这是我的尝试 登录后我不小心丢弃了浏览器吗? 问题答案: 您可能只在处理计时问题。硒移动 非常快 。比您作为用户进行交互的方式要快得多。因此,它通常以似乎无法预测的方式起作用。但这仅仅是因为Seleniu