88

I'm trying to create a simple spring boot web project with security. I can launch the application fine and the security is working fine. However, I have some components that I want to test without security (or test at all -- I cant get the test working at all).

I get an exception indicating that it can't find an ObjectPostProcessor and thus can't bring up the container.

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency

14:01:50.937 [main] ERROR o.s.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fmpdfApplication.ApplicationSecurity': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setObjectPostProcessor(org.springframework.security.config.annotation.ObjectPostProcessor); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) ~[spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) ~[spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686) ~[spring-boot-1.2.4.RELEASE.jar:1.2.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320) ~[spring-boot-1.2.4.RELEASE.jar:1.2.4.RELEASE]
    at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:103) [spring-boot-1.2.4.RELEASE.jar:1.2.4.RELEASE]
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:200) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:259) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:261) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:219) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) [junit-rt.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) [idea_rt.jar:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setObjectPostProcessor(org.springframework.security.config.annotation.ObjectPostProcessor); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:649) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    ... 43 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.config.annotation.ObjectPostProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:606) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    ... 45 common frames omitted

I'm not even trying to test anything related to web or security or anything. I'm just unit testing one of my components. My Unit test (in groovy) is like:

@RunWith(SpringJUnit4ClassRunner)
@SpringApplicationConfiguration(classes = FmpdfApplication)
@ActiveProfiles(["test", "mockstore"])
class PdfUpdaterTest {

    @Resource PdfUpdater pdfUpdater
    ...

And my (relevant) gradle dependencies are:

compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-jdbc")
testCompile("org.springframework.boot:spring-boot-starter-test")

I have tried setting management.security.enabled=false security.basic.enabled=false But that didn't help

One other relevant bit of info: I needed to customize the security so I followed the pattern to:

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
    ..

Is this part of the problem? Is there a way to make this @Lazy if that's related?

Update: If I mark the unit test as @WebIntegrationTest then everything works -- but it starts up an embedded tomcat server. How can I disable spring security for unit testing non web things?

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
steve_ash
  • 1,184
  • 1
  • 10
  • 16
  • Is FmpdfApplication mock/test or real Application class? – ikumen Jul 02 '15 at 04:46
  • If you need to disable security then it's not an unit test. It's an integration test. – Justinas Jakavonis Feb 01 '19 at 09:21
  • 2
    I believe there is little value in being pedantic about unit vs integration test definitions. I tend to focus more on the right balance of risk mitigation, cost, coupling, ability to evolve to future changes -- and not on dogma But since this little comment thread was brought to the door: i think Justas has it flipped. Security infra is a multi-component thing (your authentication system, your authorization system); integration tests are the things that cover multiple components whereas unit tests are focused on a single thing (and typically mock out the other dependent components). – steve_ash Jan 17 '20 at 18:49

15 Answers15

64

UPDATE to ANSWER: Another option i recently learned if I am using MockMvc and AutoConfigureMockMvc to test my controllers, i can just set secure=false on it to disable any security applicable to your controllers.

@AutoConfigureMockMvc(secure = false)

If not go with below for basic auth.


The exception you get is very different than what i was getting but if you want to disable the security while running test cases, you can trying using profiles and disabling the basic security using properties for test profile. This is what i did -

  1. Added annotation @Profile(value = {"development", "production"}) to my implementation of WebSecurityConfigurerAdapter -
    @Configuration
    @EnableWebSecurity
    @Profile(value = {"development", "production"})
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

2. Now, in test/resources, create application-test.yml to define properties for test profile and add this -

    # Security enable/disable
    security:
      basic:
        enabled: false

3. Now, to your test cases, add this annotation to apply the active profile @ActiveProfiles(value = "test"). This is how my class looked -

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    @ActiveProfiles(value = "test")
    @IntegrationTest({"server.port=0"})
    public class SampleControllerIntegrationTest {

Doing this disabled the security for my test cases and i was able to access the authenticated urls. I hope this works for you too. Best of luck!!!

Vikram Gulia
  • 903
  • 10
  • 17
55

FmpdfApplication is likely annotated with @EnableAutoConfiguration (or with @SpringBootApplication which is meta-annotated with @EnableAutoConfiguration), and this will lead to Spring Security being picked up and configured via auto-configuration.

If you want to see what's being auto-configured, launch your web app and access the autoconfig endpoint (e.g., http://localhost:8080/autoconfig). Then search for 'Security' to see which 'AutoConfiguration' classes are being detected.

You can then disable auto-configuration of security by excluding those classes like this:

@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementSecurityAutoConfiguration.class })

Of course, you won't want to exclude them for production deployments. Thus you'll need to have a separate @Configuration class for production and tests.

Regards,

Sam

p.s. You might also find my answer to the following question useful as well: Spring-Boot module based integration testing

Community
  • 1
  • 1
Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • 2
    Just to note that the @EnableAutoConfiguration(exclude = {SecurityAutoconfiguration.class, ManagementWebSecurityAutoConfiguration.class}) will be on your @SpringBootApplication annotated class in src/test/java. Also note the use of ManagementWebSecurityAutoConfiguration.class (I'm using spring boot 1.3.0) – Nico de Wet Nov 25 '15 at 20:03
  • 3
    FYI, I think the /autoconfig thing only works if you have actuator pulled into your pom. Since I don't have it, I put `logging.level.org.springframework=DEBUG` in src/test/resources/application.properties to see a report of what the auto configuration brought in. – bmauter Dec 12 '19 at 20:41
  • For SpringBoot 2.7.x it must be "ManagementWebSecurityAutoConfiguration.class", see the "Web" part. – Stefan May 30 '22 at 07:34
54

In my case, @AutoConfigureMockMvc(addFilters = false) helped me.

Example:

@WebMvcTest(MyController.class)
@AutoConfigureMockMvc(addFilters = false)
@TestPropertySource(locations="classpath:application-test.properties")
public class MyControllerTests {

Cheers

jumping_monkey
  • 5,941
  • 2
  • 43
  • 58
  • 1
    Exactly what I was looking for! Had changed from using Mockito with setting up the MockMvc object manually before each test to using these annotations in order to load more of the configuration beans that set up web mvc for the production app that may impact the controller behavior (jackson ObjectMapper config for instance) without having to additionally set these up manually. – GameSalutes Jul 29 '20 at 22:33
  • 2
    with this method i got Failed to load ApplicationContext – vinicius gati Oct 20 '20 at 11:35
11

Try this one to disable the spring boot security for testing


@AutoConfigureMockMvc(secure = false)

@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
@AutoConfigureMockMvc(secure = false)
public class SampleControllerIntegrationTest {
Vivek Tiwari
  • 437
  • 4
  • 9
10

Have a try with Spring Security @WithMockUser to mock a user quickly in tests.

http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test

Hantsy
  • 8,006
  • 7
  • 64
  • 109
4

For me, I updated my test annotations to fix it. It works ok now.

From:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc

To:

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
@ActiveProfiles(value = "test")
@AutoConfigureMockMvc(addFilters = false)
Kami
  • 41
  • 2
2

If your application wasn't web base but you need spring-security jar as dependency and you don't want spring-boot's auto configurations for spring-security while testing, you can add

@SpringBootTest(webEnvironment = WebEnvironment.NONE)

in your test class.

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
Cataclysm
  • 7,592
  • 21
  • 74
  • 123
2

You can just add this annotation to your test class: @AutoConfigureMockMvc(addFilters = false)

1

If you want to solve the problem using the application.yml you could do the following (solution is same as the accepted answer of Sam Brannen but with property file). If not yet present, create an application.yml in the test-folder.

   spring:
     profiles:
       active: integration-test
   autoconfigure:
     exclude:
      - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
      - org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration

Then you would set your Integration Test to:

@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("integration-test")
Fzum
  • 1,695
  • 1
  • 12
  • 15
1

This is the solution I use in spring boot 2.7.x

This approach uses the profile mechanism.

When using test profile it will disable the security for all calls.

@Bean
@Profile("test")
public WebSecurityCustomizer webSecurityCustomizer() {
 //Since we added the Spring Security to pom.xml and the spring security default
 //Behavior is ... well to secure and block all traffic
 //This will disable the behavior when testing none secured related tests
 return web -> web.ignoring().anyRequest();
}
0

For me, the fix was to update my test annotations. I replaced:

@SpringApplicationConfiguration(classes = { MyApplication.class })
@RunWith(SpringJUnit4ClassRunner.class)

with

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = MyApplication.class)
KC Baltz
  • 1,498
  • 1
  • 13
  • 22
0

Another way can be using @ActiveProfile. Create a separate yml file like application-nosecurity.yml, with below content:

security:
  ignored: /**

Now, annotate the Test class with @ActiveProfile("nosecurity") in which you don't want to use security like below:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@ActiveProfile("nosecurity")
public class BlaBlaTest {

   ## Your Test Code 

}
Sahil Chhabra
  • 10,621
  • 4
  • 63
  • 62
0

If you have this error just on webMvcTest, you must exclude security class configuration for these tests :

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile(value = {"!test"})
public class XXXX extends WebSecurityConfigurerAdapter {
A. Hafid
  • 429
  • 6
  • 14
0

Although I understand you wish to disable security, there is one annotation from Mockito allowing you to mock user

@WithMockUser(username = "Test", authorities = {"USER"})

You can use it with class to mock all methods, as well as with methods. Method will override class.

By doing this you can test your admin- specific methods. I find it usefull.

In controller tests you might be forced to mock userService as well as password encoder like below

@ExtendWith(SpringExtension.class)
@WebMvcTest(Controller.class)
@WithMockUser(username = "Test", authorities = {"USER"})
class ControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    AppUserService userService;

    @MockBean
    PasswordEncoder passwordEncoder;

    @MockBean
    SomeBusinessService someBusinessService;
Pioter88
  • 133
  • 1
  • 6
0

Nothing worked for me until I finally found this solution:

@Bean
@Profile("integration-test")
public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring()
            .antMatchers("/**");
}

And use profile "integration-test" for tests.