Reputation: 32054
I'm using Spring 3.1.4, and trying to write some integration tests around our authentication with MockMvc.
One of the root problems I'm running into is that because I'm not using Spring 3.2, I can't @Autowire
a WebApplicationContext
object in my test, and thus can't use the MockMvcBuilders.webApplicationContextSetup()
, so I'm using the xmlConfigSetup
instead.
I seem to have hit a path with multiple forks, neither of which solve all my problems.
I have things configured like so:
@ContextConfiguration(locations = {
"classpath:/test-applicationContext.security.xml",
"classpath:/test-mvc-dispatcher-servlet.xml"
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecurityTests extends AbstractJUnit4SpringContextTests {
public static final String[] CONTEXT_CONFIG = {
"classpath:/test-applicationContext.security.xml", "classpath:/test-mvc-dispatcher-servlet.xml"
};
@Autowired
private Filter springSecurityFilterChain;
@Before
public void setUp() {
ContextMockMvcBuilder xmlConfigSetup = MockMvcBuilders.xmlConfigSetup(CONTEXT_CONFIG);
this.mockMvc = xmlConfigSetup.addFilters(springSecurityFilterChain).build();
}
The advantage here is that my springSecurityFilterChain
is @Autowired
, making it easy to provide to addFilters()
. The disadvantage is that any other autowired beans are different instances than the ones in my MockMvc configuration, since I'm essentially building my servlet context twice. This means that if I autowire a UserDetailsService
and tweak it in my integration test (add a user "bob"), the MockMvc instance doesn't have it.
Option 1: Using the above config, can I access any of the beans inside the MockMvc instance? I haven't found a way, which makes "prepping" for any integration test impossible.
Option 2: Remove the @ContextConfiguration
and just let the MockMvc drive the tests. This seems cleaner, but I can't figure out how to then create/inject a Spring Security filter chain - as it's no longer autowired. (None of my beans are - which makes accessing other critical beans like the UserDetailsService
problematic as well.)
Option 3: Can I rig up a WebApplicationContext
manually that wraps from the applicationContext
in the AbstractJUnit4SpringContextTests
superclass, and provide this to the MockMvcBuilders.webApplicationContextSetup()
method? This has the advantage of not needing two separate servlet contexts, but it seems especially hacky to build this up manually when I don't really have one - and I'm not sure how to integrate the Spring Security filter chain into this either.
I'm looking for advice on which (if any) of the above options are most feasible, and how to accomplish them.
Unfortunately upgrading to a newer version of Spring is not an option.
Upvotes: 2
Views: 1996
Reputation: 20135
This post from the Spring team proposes a way to inject the WebApplicationContext
into JUnit tests. They use a custom context loader specifically implemented for running integration tests, in addition to specifying the configuration location. The trouble is that their context loader depends on a class that is no longer available in any of the Spring repositories. However, it can be derived from some of the Spring MVC Test Samples.
Step 1: Create a custom context loader
class TestWebContextLoader extends AbstractContextLoader { ... }
This context loader will be used to load your Spring configuration files.
Step 2: Use the custom loader to load the Spring configuration files.
Change
@ContextConfiguration(locations = {
"classpath:/test-applicationContext.security.xml",
"classpath:/test-mvc-dispatcher-servlet.xml" })
to
@ContextConfiguration(loader = TestWebContextLoader.class,
locations = {
"classpath:/test-applicationContext.security.xml",
"classpath:/test-mvc-dispatcher-servlet.xml" })
Step 3: Inject
WebApplicationContext
into your JUnit tests
public class SecurityTests extends AbstractJUnit4SpringContextTests {
@Autowired
private WebApplicationContext webApplicationContext;
}
Step 4: Build a mock using the injected
WebApplicationContext
public class SecurityTests extends AbstractJUnit4SpringContextTests {
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setUp() {
MockMvc mock = MockMvcBuilders.webApplicationContextSetup(webApplicationContext).build();
}
}
I have created a sample application that demonstrates the concept and where the context loads successfully.
Upvotes: 6