简体   繁体   中英

Struts 2.5 Junit test setup with spring integration

struts version: 2.5.25

spring version: 4.3.26

Using: struts2-rest-plugin, struts2-spring-plugin, JUnit 4

I am looking for assistance in correcting my unit test configuration which is currently throwing an exception "ServletContext must not be null". Below is the stack trace:

Unable to instantiate Action, com.hs.iws.actions.security.LoginAction,  defined for 'login' in namespace '/security'Error creating bean with name 'com.hs.iws.actions.security.LoginAction': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: ServletContext must not be null
    at com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:320)
    at com.opensymphony.xwork2.DefaultActionInvocation.init(DefaultActionInvocation.java:401)
    at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:201)
    at com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:76)
    at org.apache.struts2.rest.RestActionProxyFactory.createActionProxy(RestActionProxyFactory.java:50)
    at org.apache.struts2.StrutsJUnit4TestCase.getActionProxy(StrutsJUnit4TestCase.java:156)
    at com.hs.iws.actions.security.LoginActionTest.testValidation(LoginActionTest.java:41)
    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:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    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:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
    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:498)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
    at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.hs.iws.actions.security.LoginAction': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: ServletContext must not be null
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:137)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1611)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:398)
    at com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:201)
    at com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:169)
    at com.opensymphony.xwork2.ObjectFactory.buildBean(ObjectFactory.java:177)
    at com.opensymphony.xwork2.factory.DefaultActionFactory.buildAction(DefaultActionFactory.java:40)
    at com.opensymphony.xwork2.ObjectFactory.buildAction(ObjectFactory.java:142)
    at com.opensymphony.xwork2.DefaultActionInvocation.createAction(DefaultActionInvocation.java:301)
    ... 43 more
Caused by: java.lang.IllegalArgumentException: ServletContext must not be null
    at org.springframework.util.Assert.notNull(Assert.java:134)
    at org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(WebApplicationContextUtils.java:108)
    at org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(WebApplicationContextUtils.java:98)
    at com.hs.iws.actions.IwsActionSupport.getSpringContext(IwsActionSupport.java:166)
    at com.hs.iws.actions.IwsActionSupport.setupActionSupport(IwsActionSupport.java:112)
    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:498)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134)
    ... 52 more

Unit Test (spring beans are configured via annotations, we are not using any xml)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = LoginActionSpringConfig.class)
public class LoginActionTest extends IwsStrutsSpringTestCase<LoginAction> {
    
    // THIS TEST RUNS OKAY
    @Test
    public void getActionMapping() {
        ActionMapping mapping = getActionMapping("/security/login");
        Assert.assertNotNull(mapping);
        Assert.assertEquals("/security", mapping.getNamespace());
        Assert.assertEquals("login", mapping.getName());
    }


    // THIS TEST PRODUCES THE ERROR WHEN CALLING getActionProxy()
    @Test
    public void testValidation() {
        
        // the login uses the create() which expects the POST method
        request.setMethod("POST");

        ActionProxy proxy = getActionProxy("/security/login");
        Assert.assertNotNull(proxy);

        LoginAction action = (LoginAction)proxy.getAction();
        Assert.assertNotNull(action);

        Assert.assertTrue("expected some field errors", !action.getFieldErrors().isEmpty());
    }
}

LoginActionSpringConfig.class

@Configuration
public class LoginActionSpringConfig {

    @Bean
    public IIwsAuthenticationService authenticationService() {
        return new IwsAuthenticationServiceTest();
    }

    @Bean
    public ISecurityLogDao securityLogDao() {
        return new SecurityLogDaoTest();
    }

    @Bean
    public IUsersDepartmentDao usersDepartmentDao() {
        return new UsersDepartmentDaoTest();
    }

    @Bean
    public IDepartmentDao departmentDao() {
        return new DepartmentDaoTest();
    }

    @Bean
    public IwsCookieInterface iwsCookie() {
        return new IwsCookieTest();
    }

}

My web application currently uses the org.springframework.web.context.ContextLoaderListener in the web.xml

<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

and then specifies this contextClass

<context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

I don't know for sure, but I have a suspicion that I need to do something additional in my unit testing configuration to account for this. I have been unable to find the answer up to this point.

There is a servletContext field in StrutsJUnit4TestCase, which I have confirmed is NOT null. However, I suspect this is there to satisfy struts only and not spring. Can someone please assist in pointing me in the right direction to configure this missing ServletContext when testing my Struts actions through Junit 4?

Update 11/1 Below is the screen shot of the debugger. I was trying to say that I believe there are two ServletContext one in struts and one in spring. It looks like the struts test has populated the servletContext in the setup. Is there something missing in my configuration to satisfy the null ServletContext for spring? LoginActionTest 调试会话

Update 11/1 I have figured out where the error is coming from. The LoginAction being tested is inheriting this method which is being called in the super class.

protected WebApplicationContext getSpringContext() {
        return WebApplicationContextUtils
                .getWebApplicationContext(ServletActionContext.getServletContext());
    }

The ServletActionContext.getServletContext() is returning the null, which is the cause of the issue. How do I wire up ServletActionContext in the unit test to behave as expected?

I was able to resolve the issue by overriding the StrutsJunit4TestCase.getActionProxy(). My current action setup is trying to get access to the WebApplicationContext when the action is constructed, which is happening prior to the current getActionProxy() injecting the necessary objects into the ServletActionContext. Below is my override that resolved this issue

    @Override
protected ActionProxy getActionProxy(String uri) {

    // the super.getActionProxy() already sets this, but does it at the end of the method.
    // the ActionProxy proxy = ... line is already expecting it to be present and produces a NPE
    ServletActionContext.setServletContext(servletContext);
    ServletActionContext.setRequest(request);
    ServletActionContext.setResponse(response);

    return super.getActionProxy(uri);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM