Spring Boot integration test ignoring secure=false

2020-02-06 07:28发布

问题:

Can't make my @SpringBootTest work. It says authentication is on, which I do not want.

I've set it up with @AutoConfigureMockMvc(secure = false)

I submit a mock request with some JSON and my integration test should test the whole stack, taking it through the web layer with SDR to JPA and then into the in-memory database, so I can test for it using JdbcTemplate.

But the response is 401, requires authentication. Why isn't the @AutoConfigureMockMvc(secure = false) enough? What's missing?

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = { TestDataSourceConfig.class })
@EnableAutoConfiguration
@AutoConfigureMockMvc(secure = false)
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
@Transactional
public class SymbolRestTests  {

    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private SymbolRepository symbolRepository;
    @PersistenceContext
    private EntityManager entityManager;  

    @Test
    public void shouldCreateEntity() throws Exception {

        String testTitle = "TEST.CODE.1";
        String testExtra = "Test for SymbolRestTests.java";
        String json = createJsonExample(testTitle, testExtra, true);
        log.debug(String.format("JSON==%s", json));
        MockHttpServletRequestBuilder requestBuilder =
                post("/symbols").content(json);
        mockMvc.perform(requestBuilder)
                .andExpect(status().isCreated())
                .andExpect(header().string("Location",
                        containsString("symbols/")));
        entityManager.flush();
        String sql = "SELECT count(*) FROM symbol WHERE title = ?";
        int count = jdbcTemplate.queryForObject(
                sql, new Object[]{testTitle}, Integer.class);
        assertThat(count, is(1));
    }

Output logging:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /symbols
       Parameters = {}
          Headers = {}

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 = Full authentication is required to access this resource
          Headers = {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], 
                     Strict-Transport-Security=[max-age=31536000 ; includeSubDomains], 
                     WWW-Authenticate=[Basic realm="Spring"]}
         Content type = null
                 Body = 
        Forwarded URL = null
       Redirected URL = null
              Cookies = []

I discovered from Spring Boot Integration Test Results in 401 that I can disable security via properties with this:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    classes = { TestDataSourceConfig.class },
    properties = {
            "security.basic.enabled=false"
    })

but really the @AutoConfigureMockMvc(secure = false) should work, so what's blocking it?

回答1:

Adam.

Since I also recently ran into this problem after updating Spring Boot to 2.1.3.RELEASE and Spring Framework to 5.1.4.RELEASE, which forces to add Spring Web Security and If someone wants to not provide security resolver then they are required to disable security in Test Environment, so I decided to share how I ended up resolving this issue.

I was also scratching head while working up an application and writing Integration Test Cases with @SpringBootTest. Using @WebMvcTest is way less painful than this, tbh.

In my case following worked.

@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)//This annotation was required to run it successfully
@DisplayName("UserControllerTest_SBT - SpringBootTest")
class UserControllerTest_SBT extends BaseTest_SBT {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void getUsersList() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.get("/user/listAll")
            .accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andDo(print());

    }

}

@ExtendWith(SpringExtension.class) //This is not mandatory
@SpringBootTest
@AutoConfigureMockMvc(secure = false) // Secure false is required to by pass security for Test Cases
@ContextConfiguration //This is also not mandatory just to remove annoying warning, i added it
public class BaseTest_SBT {

}

What didn't work:

1- @SpringBootTest(properties = {"security.basic.enabled=false"}) <-- This solution is deprecated! More details here

2- \src\test\resources\application.properties** -> security.basic.enabled=false

Hopefully, this will be helpful to someone.