Struts 1 + Spring 3 + Maven + Интеграционный тест: ошибка компонента Scope Session

Я интегрирую старое приложение Struts 1 с Spring 3 через разработку через тестирование (TDD). По этой причине мои интеграционные тесты так важны. Я пытаюсь реализовать интеграционный тест действия с помощью "Struts Test Case". Действие (HomeAction) автоматически связывается с одноэлементным сервисом и сессионным компонентом (Basket). Но когда я запускаю тест, я получаю следующую ошибку:

Ошибка создания бина с именем scopedTarget.basket: область действия "сессия" не активна для текущего потока; рассмотрите возможность определения прокси-объекта с областью видимости для этого компонента, если вы намереваетесь ссылаться на него из синглтона; Вложенное исключение - java.lang.IllegalStateException: не найден привязанный к потоку запрос: Вы ссылаетесь на атрибуты запроса вне фактического веб-запроса или обрабатываете запрос вне первоначально получающего потока? Если вы на самом деле работаете с веб-запросом и по-прежнему получаете это сообщение, ваш код, вероятно, выполняется за пределами DispatcherServlet/DispatcherPortlet: в этом случае используйте RequestContextListener или RequestContextFilter для предоставления текущего запроса.

Сервис внедряется, а сессионный компонент - нет. Если я пытаюсь запустить приложение, оно работает нормально.

Кто-нибудь знает, как это решить? Я не знаю, является ли это проблемой конфигурации maven или нет, но, как показывает тест, веб-контекст не загружается, как при выполнении приложения. Заранее спасибо.

Это код:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>
            org.apache.struts.action.ActionServlet
        </servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>
             /WEB-INF/struts-config.xml
            </param-value>
        </init-param>
        <init-param>
            <param-name>autowire</param-name>
            <param-value>byName</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
           <servlet-name>action</servlet-name>
           <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

распорки-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">


<struts-config>

    <!-- ========== Definiciones de Form Bean =================== -->
    <form-beans>
        <form-bean name="homeForm" type="org.examples.appname.web.action.HomeForm" />
    </form-beans>


    <!-- ==========Forward's Globales ============================== -->
    <global-forwards>
        <forward name="error" path="/WEB-INF/views//error.jsp" />
    </global-forwards>


    <!-- ========== Mapeo de Acciones ============================== -->
    <action-mappings>
        <action path="/" type="org.apache.struts.actions.ForwardAction" parameter="/home"/>
        <action path="/home" type="org.examples.appname.web.action.HomeAction" name="homeForm" scope="request">
            <forward name="success" path="/WEB-INF/views/home.jsp" />
        </action>
    </action-mappings>


    <!-- ========== Controller Configuration ======================== -->
    <controller> 
        <!-- Autowaring injection of actions: You don't need to declare them on action-servlet.xml -->
        <set-property property="processorClass" value="org.springframework.web.struts.AutowiringRequestProcessor" />
    </controller>


    <!-- ========== Message Resources Definitions ==================== -->
    <message-resources parameter="org.example.appname.ApplicationResources" />


    <!-- ========== Plugins configuration ==================== -->
    <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
        <set-property property="contextConfigLocation" value="/WEB-INF/action-servlet.xml, /WEB-INF/root-context.xml"/>
    </plug-in>

</struts-config>

action-servlet.xml пуст, потому что действия являются автоматически связанными.

<!-- Action Context: defines all web actions -->
<beans></beans>

Spring Context: root-context.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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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/aop 
         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="org.examples.appname" scoped-proxy="targetClass"/>


    <bean name="MenuService" class="org.examples.appname.core.service.MenuServiceImpl" scope="singleton"/>

</beans>

HomeAction.java

@Component
public class HomeAction extends LookupDispatchAction {

    @Autowired
    private Basket basket;

    private MenuService menuService;

    public void setMenuService(MenuService menuService) {
        this.menuService = menuService;
        System.out.println(this.toString() + " - " + this.menuService.toString());
    }



    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        ActionErrors errors = new ActionErrors();

        HttpSession session = request.getSession();

        System.out.println("request " + request.hashCode());
        System.out.println("Session " + session.getId() + " - " + new Date(session.getCreationTime()));
        System.out.println("Basket " + basket.getState());

        //store an object on the request
        request.setAttribute("MenuItems", menuService.getMenuItems());

        // Forward control to the specified success URI
        return mapping.findForward("success");

    }


    @Override
    protected Map getKeyMethodMap() {
        // TODO Auto-generated method stub
        return null;
    }
}

Basket.java

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Basket{

    private final String state;

    public Basket() {

        this.state = UUID.randomUUID().toString();

    }

    public String getState() {
        return state;
    }
}

HomeIntegrationTest.java

public class HomeIntegrationTest extends MockStrutsTestCase{

    private static final String FORWARDED_URL = "/home";
    private static final String VIEW = "/WEB-INF/views/home.jsp";


    @Before
    public void setUp() throws Exception {
        super.setUp();
    }


    @Test
    public void testIndexUrlforwardsCorrectly() throws Exception {
        setRequestPathInfo("/");
        actionPerform();
        verifyForwardPath(FORWARDED_URL);
        verifyNoActionErrors();
    }

    @Test
    public void testHomeUrlforwardsCorrectly() throws Exception {
        setRequestPathInfo("/home");
        actionPerform();
        verifyForwardPath(VIEW);
        assertEquals("Menu items", getRequest().getAttribute("MenuItems"));
        verifyNoActionErrors();
    }

}

Maven pom.xml

<testResources>
  <testResource>
    <directory>src/test/java</directory>
    <includes>
      <include>*.*</include>
    </includes>
  </testResource>
  <testResource>
    <directory>src/main/webapp/WEB-INF</directory>
    <targetPath>/WEB-INF</targetPath> -->
    <includes>
      <include>*.xml</include>
    </includes>
  </testResource>
</testResources>

2 ответа

Давным-давно я написал блог о юнит-тестировании стоек приложений с Maven. Вместо того чтобы копировать всю историю здесь, я бы хотел обратиться к тестовым приложениям Struts JUnit с фиктивными объектами и Maven. Надеюсь, это поможет.

Как я уже говорил выше, я решил эту проблему, следуя этой ссылке и добавив в весенний контекст xml (root-context.xml) этот компонент,

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="session">
                <bean class="org.springframework.context.support.SimpleThreadScope"/>
            </entry>
        </map>
    </property>
</bean>

но я получил следующее ПРЕДУПРЕЖДЕНИЕ в среду исполнения:

'SimpleThreadScope не поддерживает обратные вызовы уничтожения. Подумайте об использовании RequestScope в веб-среде. '

Чтобы разрешить это сообщение только для тестирования, я установил другой весенний контекст xml (root-context.xml) для тестовой среды через maven pom.xml.

<build>
        <testResources>
            <testResource>
                <directory>src/main/webapp/WEB-INF</directory>
                <targetPath>/WEB-INF</targetPath>
                <includes>
                    <include>*.xml</include>
                </includes>
                <excludes>
                    <exclude>root-context.xml</exclude>
                </excludes>
            </testResource>
            <testResource>
                <directory>src/test/webapp/WEB-INF</directory>
                <targetPath>/WEB-INF</targetPath>
                <includes>
                    <include>*.xml</include>
                </includes>
            </testResource>
        </testResources>
    </build>
Другие вопросы по тегам