2017-02-19 183 views
0

我正在開發一個具有一定數量控制器的Spring Boot MVC應用程序。 我的根控制器:Spring Boot WebMvc測試不包括Spring Security

@Controller 
@RequestMapping("/") 
public class RootController { 

    @GetMapping 
    public String showStartPage() { 
     log.info("GET: Show home page"); 
     return "index"; 
    } 
} 

我已經成功地實施了控制器MVC測試。我RootController測試是:

@RunWith(SpringRunner.class) 
@WebMvcTest(RootController.class) 
public class RootControllerMvcTest { 

    @Autowired 
    private MockMvc mvc; 

    @Test 
    public void testRoot() throws Exception { 
     mvc.perform(get("/").accept(MediaType.TEXT_HTML)) 
      .andExpect(status().isOk()) 
      .andExpect(view().name("index")); 
    } 
} 

問題:

但是,當我介紹Spring Security認證和授權,所有的MVC控制器測試拋錨。根控制器測試斷言錯誤是:

java.lang.AssertionError: Status 
Expected :200 
Actual :401 

我的安全配置是:

@Configuration 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserDetailsService userDetailsService; 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .authorizeRequests() 
      .antMatchers("/", "/fonts/*").permitAll() 
      .antMatchers("/user/**").hasAuthority("ADMIN") 
      .anyRequest().fullyAuthenticated() 
      .and() 
      .formLogin() 
      .loginPage("/login") 
      .failureUrl("/login?error") 
      .usernameParameter("email") 
      .permitAll() 
      .and() 
      .logout() 
      .logoutUrl("/logout") 
      .deleteCookies("remember-me") 
      .logoutSuccessUrl("/") 
      .permitAll() 
      .and() 
      .rememberMe(); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
      .userDetailsService(userDetailsService) 
      .passwordEncoder(new BCryptPasswordEncoder()); 
    } 
} 

解決方法:

然後,我設法解決這個問題:

@RunWith(SpringRunner.class) 
@SpringBootTest 
@AutoConfigureMockMvc 
public class RootControllerMvcTest { 
... 
} 

在這種情況下,我的測試會加載整個Application上下文。

我的問題是:

  1. 怎麼可能讓我從控制器的認證和授權過程,測試僅邏輯分離MVC測試?
  2. 測試認證和授權實施的最佳實踐是什麼?我是否必須爲此使用@SpringBootTest?
  3. 單獨測試我的控制器和安全邏輯是否是一個很好的決定?

謝謝。

+0

[簡單測試Spring Boot安全性]的可能重複(http://stackoverflow.com/questions/31812054/testing-spring-boot-security-simply) –

+0

謝謝。這是一個有用的鏈接。但首先,我試圖弄清楚是否可以使用MockMvc測試我的控制器,而不使用Spring Security。 –

回答

0

很多這可能是意見,但我會給我兩分錢。首先,目標應該是覆蓋面和上市時間。那就是我需要一個相當快速的解決方案來實現和生產可以在生產環境中工作的軟件。所以我不太關心意識形態。

爲此,我喜歡使用單元測試和集成測試的混合。在單元測試試圖達到接近100%的代碼覆蓋範圍的情況下,集成更多地集中於與其他系統的交互,例如,數據庫的客戶端,其他服務等。順便說一下,單元和集成測試都必須是自動化的,所以集成應該在沒有人在場的情況下完成。

我過去曾經用過兩種方法來測試我的控制器。一種是直接在Spring上下文外調用controllers方法。我們能夠達到我們的代碼覆蓋率目標。這工作了一段時間,但當我們開始使用Spring的HATEOS特性時失敗了,HATEOS特性依賴於使用Spring MVC上下文來創建HATEOS所基於的鏈接。所以我們切換到MockMvc進行單元測試。這工作,我們達到了我們的覆蓋目標。現在在你的情況下,問題是安全性正在妨礙你的單元測試。我希望側重安全配置,以便達到我的覆蓋目標,可以在集成測試中測試安全性。這可以通過在SecurityConfig課上使用@Profile註釋來完成。在我們的微服務網絡中,我們使用@Profile來啓用和禁用配置類。例如,如果使用某個配置文件,我們僅向我們的Eureka服務器註冊。有不同的方法可以做到這一點。一種方法是遵循我們爲我們的Eureka配置所做的事情,它只有在我們稱爲active-eureka的配置文件存在的情況下才能啓用,我們創建了該名稱。這可以防止在我們運行我們的IDE中的微服務時聯繫Eureka服務器。在你的情況下,你可以有一個名爲activate-security的配置文件。這將允許你用安全性破壞樂趣來做基於MockMvc的單元測試。

現在隨着單元測試的結束,您仍然必須針對您的服務啓用安全性進行集成測試。這有不同的方法。我們使用的一種方法是將@SpringBootTest註釋與TestRestTemplate類一起使用。這啓動我們的微服務,然後我們使用TestRestTemplate進行真正的服務呼叫。 TestRestTemplate是一個真正的http RestTemplate客戶端,只是它知道你的Web服務運行在哪個端口上,所以你可以在試圖找出端口時刪除一些鍋爐板代碼。通過TestRestTamplte,您可以忽略網址的http://host:port部分。它被描述爲here。請注意,如果我們像您一樣啓用了安全性,那麼我們需要像安全生產中的真實客戶那樣通過安全性,這意味着我們不會側重安全性,而是積極參與安全性,這與此相反我們在單元測試中做了什麼。

我們的單元測試和集成測試有不同的目標。使用上述方法,我們可以獨立地專注於這些目標。

+0

謝謝@Jose的詳細解答。我用'@Profile'分離了我的控制器測試邏輯。不過,我的實施方式略有不同。您的最佳實踐和集成測試建議非常有用!他們將幫助我實施我的集成測試。 –

0

答1:


我已經排除從我的MVC測試Spring Security的特點如下:

  1. 新增src/test/java/resources/application-disabled-security.yml

    安全: 基礎: 啓用:假

  2. 向mvc測試添加了@ActiveProfiles("disabled-security")註釋。

0

以前提出的方法在彈簧的新版本中似乎是deprecated

另一方面,現在可以使用相同的功能作爲附加參數。

例如,在我的情況與WebMvcTest我從

@RunWith(SpringRunner.class) 
@WebMvcTest(ImportController.class) 

搬到

@RunWith(SpringRunner.class) 
@WebMvcTest(value = ImportController.class, secure = false) 

和所有的工作只是罰款。