我试图使用spring security和一个简单的home(root)控制器在spring-boot中运行单元测试,该控制器使用thymeleaf进行模板处理。我正在尝试编写一些单元测试,以验证我的安全权限是否正常工作,以及正确的数据是否隐藏或显示在我的模板(使用thymeleaf Spring Security集成)中。当我运行它时,应用程序本身确实可以正常工作。我只想通过一组集成测试来验证它是否工作。您可以在这里找到所有的代码,但我也将在下面包括相关的代码片段:
https://github.com/azeckoski/lti_starter
这个控制器非常简单,除了呈现模板(在根部--即“/”)之外什么也不做。
@Controller
public class HomeController extends BaseController {
@RequestMapping(method = RequestMethod.GET)
public String index(HttpServletRequest req, Principal principal, Model model) {
log.info("HOME: " + req);
model.addAttribute("name", "HOME");
return "home"; // name of the template
}
}
模板中有很多内容,但与测试相关的位是:
<p>Hello Spring Boot User <span th:text="${username}"/>! (<span th:text="${name}"/>)</p>
<div sec:authorize="hasRole('ROLE_USER')">
This content is only shown to users (ROLE_USER).
</div>
<div sec:authorize="isAnonymous()"><!-- only show this when user is NOT logged in -->
<h2>Form Login endpoint</h2>
...
</div>
最后是测试:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class AppControllersTest extends BaseApplicationTest {
@Autowired
WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilter;
private MockMvc mockMvc;
@Before
public void setup() {
// Process mock annotations
MockitoAnnotathtml" target="_blank">ions.initMocks(this);
// Setup Spring test in webapp-mode (same config as spring-boot)
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.addFilter(springSecurityFilter, "/*")
.build();
}
@Test
public void testLoadRoot() throws Exception {
// Test basic home controller request
MvcResult result = this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andReturn();
String content = result.getResponse().getContentAsString();
assertNotNull(content);
assertTrue(content.contains("Hello Spring Boot"));
assertTrue(content.contains("Form Login endpoint"));
}
@Test
public void testLoadRootWithAuth() throws Exception {
Collection<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication authToken = new UsernamePasswordAuthenticationToken("azeckoski", "password", authorities);
SecurityContextHolder.getContext().setAuthentication(authToken);
// Test basic home controller request
MvcResult result = this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andReturn();
String content = result.getResponse().getContentAsString();
assertNotNull(content);
assertTrue(content.contains("Hello Spring Boot"));
assertTrue(content.contains("only shown to users (ROLE_USER)"));
}
}
我在上述两项测试中得到的错误是:
但是,只有在启用了两个测试并且包含了springSecurityFilter时,才会出现这种情况。如果禁用其中一个测试并删除springSecurityFilter代码(.addFilter(springSecurityFilter,“/*”)
),则不再出现该错误。我怀疑可能有什么东西把WebApplicationContext搞乱了,或者使安全内容处于某种故障状态,但我不确定需要重置或更改什么。
因此,如果我取出第二个测试台,删除springSecurityFilter,那么我的第一个测试仍然会失败(这个测试特别是AssertTrue(content.contains(“Form Login Endpoint”))
),但我不再得到任何错误。当我查看生成的HTML时,没有看到任何使用sec:authorize
属性的标记内容。
所以我四处搜索,发现了一个建议,我需要在SpringSecurityFilter
中添加(我在上面的代码示例中已经做了),然而,一旦我这样做,我就会立即得到失败(它甚至不会到达没有它就会失败的地步)。关于是什么导致了这个异常以及如何修复它,有什么建议吗?
我有一个变通的解决方案,它似乎完全解决了这个问题,适用于Spring-boot:1.1.4、Spring-security:3.2.4和Thymeleaf:2.1.3(尽管它有点麻烦)。
这是修改后的单元测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class AppControllersTest {
@Autowired
public WebApplicationContext context;
@Autowired
private FilterChainProxy springSecurityFilter;
private MockMvc mockMvc;
@Before
public void setup() {
assertNotNull(context);
assertNotNull(springSecurityFilter);
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in webapp-mode (same config as spring-boot)
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilter)
.build();
context.getServletContext().setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
}
...
这里的妙处是强制webapplicationcontext.root_web_application_context_attribute
成为实际的web应用程序上下文(我注入了它)。这允许实际的sec:属性工作,但是我尝试设置权限以便用户登录的第二个测试没有通过(看起来用户仍然是匿名的)。
public MockHttpSession makeAuthSession(String username, String... roles) {
if (StringUtils.isEmpty(username)) {
username = "azeckoski";
}
MockHttpSession session = new MockHttpSession();
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
Collection<GrantedAuthority> authorities = new HashSet<>();
if (roles != null && roles.length > 0) {
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
}
//Authentication authToken = new UsernamePasswordAuthenticationToken("azeckoski", "password", authorities); // causes a NPE when it tries to access the Principal
Principal principal = new NamedOAuthPrincipal(username, authorities,
"key", "signature", "HMAC-SHA-1", "signaturebase", "token");
Authentication authToken = new UsernamePasswordAuthenticationToken(principal, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authToken);
return session;
}
类创建主体
(通过ConsumerCredentials提供OAuth支持)。如果不使用OAuth,那么可以跳过ConsumerCredentials部分,只实现主体(但应该返回GrantedAuthority的集合)。
public static class NamedOAuthPrincipal extends ConsumerCredentials implements Principal {
public String name;
public Collection<GrantedAuthority> authorities;
public NamedOAuthPrincipal(String name, Collection<GrantedAuthority> authorities, String consumerKey, String signature, String signatureMethod, String signatureBaseString, String token) {
super(consumerKey, signature, signatureMethod, signatureBaseString, token);
this.name = name;
this.authorities = authorities;
}
@Override
public String getName() {
return name;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
}
然后像这样修改测试(创建会话,然后在模拟请求上设置它):
@Test
public void testLoadRootWithAuth() throws Exception {
// Test basic home controller request with a session and logged in user
MockHttpSession session = makeAuthSession("azeckoski", "ROLE_USER");
MvcResult result = this.mockMvc.perform(get("/").session(session))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andReturn();
String content = result.getResponse().getContentAsString();
assertNotNull(content);
assertTrue(content.contains("Hello Spring Boot"));
}
问题内容: 我有一个与此类似的简单带注释的控制器: 我想用这样的单元测试来测试它: 问题是AnnotationMethodHandlerAdapter.handler()方法引发异常: 问题答案: 从Spring 3.2开始,有一种适当的方法可以轻松,优雅地进行测试。您将可以执行以下操作: 有关更多信息,请访问http://blog.springsource.org/2012/11/12/spri
我对使用Spring控制器进行单元测试的概念是新的。我正在遵循我在网上找到的一些示例,并尝试实现他们的测试策略。这是我的基本控制器: 这是我的单元测试: 看起来很简单,但我得到了以下错误: 它完成了这项工作,但它没有像我之前尝试的那样使用任何Spring注释…这种方法是不好的,所以试图弄清楚为什么每当我在测试文件中包含注释时,总是会出现错误。 我的POM:
问题内容: 对于我一生,我无法让$ httpBackend在执行$ http get请求的控制器上工作。我已经尝试了几个小时=) 我将其简化为下面可以最简单的形式。如果我通过测试 在控制器中注释掉$ http.get()请求 在测试中注释掉“ httpMock.flush()” 并更改“猪”和“狗”以匹配 也就是说,这是一个有效的工作测试和应用程序。 如果放回去,则会在底部显示错误。 app /
我有一个请求表单的映射: 现在我想用MockMvcBuilders为此编写一个测试。不过,我不能这样做。 这里的挑战是请求处理程序需要使用Multipart/form-data,它由4个Multipart Files和1个Json数据组成。 有没有办法解决这个问题?请记住,我必须使用Spring 4.3。 如果您需要更多信息,请告诉我。
问题内容: 我正在尝试创建一个简单的单元测试来测试我的表演功能。 我收到以下错误: 看来这不是控制器的范围吗? 这是我的控制器: 这是我的控制器单元测试: 问题答案: 为什么不简单地使用spyOn函数? 希望这可以帮助!
问题内容: 编辑:本文末尾的“快速与肮脏”解决方案 我使用的是AngularUI-Bootstrap中的模式窗口,其方式与网站上说明的相同,只是我分割了文件。因此,我有: CallingController.js: modalController.js: 当我使用Karma测试此代码(在karma配置文件中加载了 ui-bootstrap-tpls.min.js 文件)时,出现以下错误: 错误:[