我正在编写一些Spring集成测试以测试我的应用程序中的Spring Security。我使用RequestPostProcessor
创建具有不同权限的测试用户。我也缓存它们以在所有测试中重用。参见下面的代码:
public final class Users {
public static final RequestPostProcessor
ANONYMOUS = anonymous();
public static final RequestPostProcessor
PERMISSIONS_READ = buildUser(Permissions.PERMISSIONS_READ);
public static final RequestPostProcessor
PERMISSIONS_WRITE = buildUser(Permissions.PERMISSIONS_WRITE);
public static final RequestPostProcessor
PERMISSIONS_DELETE = buildUser(Permissions.PERMISSIONS_DELETE);
public static final RequestPostProcessor
ROLES_READ = buildUser(Permissions.ROLES_READ);
public static final RequestPostProcessor
ROLES_WRITE = buildUser(Permissions.ROLES_WRITE);
public static final RequestPostProcessor
ROLES_DELETE = buildUser(Permissions.ROLES_DELETE);
public static final RequestPostProcessor
USERS_READ = buildUser(Permissions.USERS_READ);
public static final RequestPostProcessor
USERS_WRITE = buildUser(Permissions.USERS_WRITE);
public static final RequestPostProcessor
USERS_DELETE = buildUser(Permissions.USERS_DELETE);
private Users() {}
private static RequestPostProcessor buildUser(Permissions permission) {
return buildUser(permission.toString(), permission.toString());
}
private static RequestPostProcessor buildUser(String name, String... authorities) {
return user(name).authorities(SecurityUtils.authoritiesFromStrings(authorities));
}
}
当我在测试中使用它们时,我得到了
ConcurrentModificationException
。用法:
....................
@Autowired private WebApplicationContext context;
@Autowired private Filter springSecurityFilterChain;
MockMvc mvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build();
....................
MockHttpServletRequestBuilder req = get("some-url");
mvc.perform(req.with(Users.ANONYMOUS))
.andExpect(status().isFound())
.andExpect(header().string("Location", "login-url"));
Stream.of(Users.PERMISSIONS_WRITE, Users.PERMISSIONS_DELETE,
Users.ROLES_WRITE, Users.ROLES_DELETE,
Users.USERS_WRITE, Users.USERS_DELETE)
.parallel()
.forEach(Unchecked.consumer(user -> mvc.perform(req.with(user)) //Exception is here and caused by .with(user)
.andExpect(status().isForbidden())));
....................
例外:
....................
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder.postProcessRequest(MockHttpServletRequestBuilder.java:754)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:145)
at com.ipan.fin.man.integrational.PermissionsTest.lambda$checkReadSecurity$5(PermissionsTest.java:162)//line with my code
....................
在
org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
中引发异常,因为我正在对其进行缓存(MockHttpServletRequestBuilder req
)并在多个流中使用。因此,以防万一,当一个流在postProcessors
上迭代时@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
for (RequestPostProcessor postProcessor : this.postProcessors) { //here
request = postProcessor.postProcessRequest(request);
if (request == null) {
throw new IllegalStateException(
"Post-processor [" + postProcessor.getClass().getName() + "] returned null");
}
}
return request;
}
第二个将新的
postProcessor
添加到postProcessors
@Override
public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) {
Assert.notNull(postProcessor, "postProcessor is required");
this.postProcessors.add(postProcessor); //here
return this;
}
ConcurrentModificationException
将被抛出。据我在上面的代码中了解,由于以下两个原因,不允许缓存
MockHttpServletRequestBuilder
:它不是线程安全的
MockHttpServletRequestBuilder.with
调用将添加新的RequestPostProcessor
,而不是像我期望的那样替换旧的。我对吗?
附言使用顺序流,测试可以正常工作,并且看起来
MockHttpServletRequestBuilder.with
的调用替换了旧的RequestPostProcessor
对象,因为我总是得到正确的测试结果(来自服务器的响应状态) 最佳答案
是的,您的分析是正确的:MockHttpServletRequestBuilder
并非设计为可以同时使用。
无论如何,创建MockHttpServletRequest
实际上并没有任何明显的开销。
因此,我建议您每次需要时就简单地创建一个新请求,并且使用并行流实际上并不会给您带来很多好处(如果有的话)。
总之,请尽量不要过度设计您的测试。 ;-)
问候,
Sam(Spring TestContext Framework的作者)