5

I have a problem with my Unit Tests for a spring Rest application. In my Unit Tests I always get the problem that I get 401 "Unauthorized" as responsestatus and I don't know how to solve this problem.

My Security config is this:

@Configuration
@EnableWebSecurity
@Order(1)
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ApiKeyRepository apiKeyRepository;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ApiKeyAuthFilter filter = new ApiKeyAuthFilter(Globals.HEADER_APIKEY);
        filter.setAuthenticationManager(authentication -> {
            String principal = (String) authentication.getPrincipal();
            Optional<ApiKey> apiKey = apiKeyRepository.findByApiKey(principal);
            if (!apiKey.isPresent()) {
                throw new BadCredentialsException("The API key was not found or not the expected value.");
            }
            authentication.setAuthenticated(true);
            return authentication;
        });
        httpSecurity.
                csrf().disable().
                addFilter(filter).authorizeRequests().anyRequest().authenticated().and().
                regexMatcher("(?i)/api/v1/(?:print|resource)(?:/|$).*").
                sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

My Unit Test looks like:

@RunWith(SpringRunner.class)
@WebMvcTest(ResourceController.class)
public class ResourceControllerTestGetByLogicalFileNameOrDate {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private ResourceRepository resourceRepositoryMock;

    private Resource setNewResource(long fileSize, String hash, String logicalFileName) {
        return getResource(fileSize, hash, logicalFileName);
    }

    @Test
    public void testGetResourceByLogicalFileNameOrDateWithNoInput() throws Exception {
        mvc.perform(get(String.format("/api/v1/resource/find/%s", ""))
                .header(Globals.HEADER_APIKEY, "lEcsQI09MjUTCMQ5IVvSjf4Anxt+c4nhZAMnU1yDmVI=")
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isBadRequest());
    }

I always get the problem that my tests failed. As response I get:

2018-05-07 09:26:52.003  INFO 18692 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2018-05-07 09:26:52.003  INFO 18692 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization started
2018-05-07 09:26:52.005  INFO 18692 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization completed in 2 ms

java.lang.AssertionError: Status 
Expected :400
Actual   :401
 <Click to see difference>


    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:55)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:82)
    at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:617)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:178)
    at at.techsoft.iprint.nano.services.resource.ResourceControllerTestGetByLogicalFileNameOrDate.testGetResourceByLogicalFileNameOrDateWithNoInput(ResourceControllerTestGetByLogicalFileNameOrDate.java:83)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    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.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    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.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:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    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:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Can i turn off the security for the tests? Please can anyone help me to solve this.

When I use the Annotation @ActiveProfiles(value = "test") I get the Exception:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /api/v1/resource/find
       Parameters = {logicalFileName=[Res]}
          Headers = {Content-Type=[application/json], X-NANO-APIKEY=[]}
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest[http://localhost/api/v1/resource/find?logicalFileName=Res]}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 401
    Error message = Unauthorized
          Headers = {WWW-Authenticate=[Basic realm="Realm"], X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status 
Expected :200
Actual   :401
 <Click to see difference>


    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:55)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:82)
    at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:617)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:178)
    at at.techsoft.iprint.nano.services.resource.ResourceControllerTestGetByLogicalFileNameOrDate.testGetResourceByLogicalFileNameOrDateWithCorrectLogicalFileName(ResourceControllerTestGetByLogicalFileNameOrDate.java:88)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    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.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    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:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    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:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Dullimeister
  • 504
  • 1
  • 7
  • 18
  • An answer in [Unit test Springboot MockMvc returns 403 Forbidden](https://stackoverflow.com/questions/53387415/unit-test-springboot-mockmvc-returns-403-forbidden/57069366#57069366) worked for me: `@AutoConfigureMockMvc(addFilters = false)`, but I'm not 100% of whether it's actually related. – ggorlen Apr 26 '22 at 01:57

2 Answers2

17

Since I use Spring Boot 1, I solved it this way:

@WebMvcTest(value = ResourceController.class, secure = false)

Please note that for the newer version of Spring Boot 2 the secure option is not available anymore, so use this:

@WebMvcTest(value = ResourceController.class,  excludeAutoConfiguration = {SecurityAutoConfiguration.class})
Vladucu Voican
  • 283
  • 1
  • 10
Dullimeister
  • 504
  • 1
  • 7
  • 18
0

Looks like your security is the one causes issue. You can use Spring Profile( like @Profile("Production")) as follows:

@Configuration
@EnableWebSecurity
@Profile("Production")
@Order(1)
public class ApiSecurityConfig{
...
}


@RunWith(SpringRunner.class)
@WebMvcTest(ResourceController.class)
@ActiveProfile("test")
public class ResourceControllerTestGetByLogicalFileNameOrDate {
...
}

Reference : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

Since in local tests you wouldn't want that to be configured, you can use @ActiveProfiles("test") while running the tests. this way, the configurations won't be loaded for local tests.

Karthik R
  • 5,523
  • 2
  • 18
  • 30
  • When I use active profile i get this Exception I added you in my qestion. – Dullimeister May 07 '18 at 08:30
  • Is the `ApiSecurityConfig` annotated with `@Profile("somename")`? Also, can you catch exception and check the trace to see if you get more info? – Karthik R May 07 '18 at 08:39
  • The `@Profile("Production")` is added to `ApiSecurityConfig`. – Dullimeister May 07 '18 at 08:43
  • What does the stacktrace say? any clue on why 401 ? Are there any other security files that need to be annotated with profiles? clearly this seems to be authentication issue. Any proxy etc.? – Karthik R May 07 '18 at 08:50
  • In the stacktrace is nothing relevant. I have 2 Security classes which extend `WebSecurityConfigurerAdapter` and bot have the annotation `@Profile(value = "Production")`. – Dullimeister May 07 '18 at 09:02
  • 1
    :( No idea then. Did you debug to check if the execution goes through security config? If it doesn't it should work without authentication even if you remove header gobal api key from test. – Karthik R May 07 '18 at 09:08