#spring-test #spring-mvc-test #spring-security-test
#spring-test #spring-mvc-test #spring-security-test
Вопрос:
Я экспериментирую с новой платформой тестирования безопасности Spring. У меня есть тест, который включает возврат объекта JPA с отложенными коллекциями.
Следующий тест заканчивается ошибкой, поскольку одна из отложенных коллекций.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ActiveProfiles({ Profiles.TEST })
@ContextConfiguration(classes = { FullSecurityTestConfiguration.class, FullIntegrationTestConfiguration.class, BaseTestConfiguration.class, WebMvcConfiguration.class,
EnableHelperComponents.class })
@TestExecutionListeners(listeners = { ServletTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, WithSecurityContextTestExcecutionListener.class })
public class CurriculumPermissionEvaluatorAuthorizationTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@After
public void clean() {
SecurityContextHolder.clearContext();
}
@Test
@WithUserDetails("balteo@yahoo.fr")
public void shouldAllowCurriculumRetrieval() throws Exception {
mockMvc.perform(get("/curriculum/findCurriculumById").param("id", "1")//
.contentType(MediaType.APPLICATION_JSON)//
.header("X-Ajax", "true"))//
.andDo(print())//
.andExpect(status().isOk());//
}
Вот сообщение об ошибке:
21:40:52.731 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
21:40:52.732 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@4a17c31]
21:40:52.733 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@4a17c31] after transaction
21:40:52.733 [main] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
21:40:52.795 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - Invoking ResponseBodyAdvice chain for body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
21:40:52.796 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - After ResponseBodyAdvice chain body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
21:40:52.819 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.820 [main] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.820 [main] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
21:40:52.821 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
21:40:52.821 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
Что я получаю неправильно в моей тестовой настройке? Почему JPA entity manager закрывается слишком рано? Как я могу обойти эту проблему?
правка 1:
Метод из контроллера:
@RequestMapping(value = "/findCurriculumById", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public Curriculum findCurriculumById(@RequestParam Long id) {
return curriculumService.findCurriculum(id);
}
Метод из сервиса:
@Override
@PreAuthorize("isAuthenticated() AND hasPermission(#curriculumId, 'curriculumByIdOwnerPermission')")
@Cacheable(value = CacheConfiguration.DATABASE_CACHE_NAME, key = "'curriculum.Id:' #curriculumId")
public Curriculum findCurriculum(Long curriculumId) {
return curriculumRepository.findOne(curriculumId);
}
правка 2:
Я понял, что тест на самом деле ошибочный, хотя я прокомментировал метод тестирования с помощью @Transactional
следующим образом:
@Test
@WithUserDetails("balteo@yahoo.fr")
@Transactional
public void shouldAllowCurriculumRetrieval() throws Exception {
mockMvc.perform(get("/curriculum/findCurriculumById").param("id", "1")//
.contentType(MediaType.APPLICATION_JSON)//
.header("X-Ajax", "true"))//
.andDo(print())//
.andExpect(status().isOk());//
}
Вот сообщение об ошибке:
16:54:31.747 [main] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@ab4a4ae, returned: 1
16:54:31.747 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Authorization successful
16:54:31.747 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - RunAsManager did not change Authentication object
16:54:31.752 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - Invoking ResponseBodyAdvice chain for body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
16:54:31.752 [main] DEBUG o.s.w.s.m.m.a.ResponseBodyAdviceChain - After ResponseBodyAdvice chain body=Curriculum [drivingLicense=true, smoker=false, maxNumberChildren=2, workExperienceInYears=11, dateOfBirth=1975-01-06 00:00:00.0, telephoneNumber=0142778899, firstName=Juliette, visible=true, validated=true]
16:54:31.767 [main] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.bignibou.domain.Curriculum.workExperiences, could not initialize proxy - no Session (through reference chain: com.bignibou.domain.Curriculum["workExperiences"])
16:54:31.768 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
16:54:31.768 [main] DEBUG o.s.t.w.s.TestDispatcherServlet - Successfully completed request
MockHttpServletRequest:
HTTP Method = GET
Request URI = /curriculum/findCurriculumById
Parameters = {id=[1]}
Headers = {Content-Type=[application/json], X-Ajax=[true]}
Handler:
Type = com.bignibou.controller.curriculum.CurriculumController
Method = public com.bignibou.domain.Curriculum com.bignibou.controller.curriculum.CurriculumController.findCurriculumById(java.lang.Long)
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 500
Error message = null
Headers = {Content-Type=[application/json]}
Content type = application/json
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
16:54:31.770 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction rollback
16:54:31.770 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Rolling back JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@76d0bc1d]
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@76d0bc1d] after transaction
16:54:31.771 [main] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Resuming suspended transaction after completion of inner transaction
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [com.bignibouX.tests.security.curriculum.CurriculumPermissionEvaluatorAuthorizationTest.clean]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3972d402] for JPA transaction
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@21bcecc5]
16:54:31.771 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
16:54:31.772 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3972d402]
16:54:31.772 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3972d402] after transaction
правка 3:
Вот соответствующая часть учебного плана:
@Entity
public class Curriculum {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "curriculum")
private Set<WorkExperience> workExperiences;
Ответ №1:
Ваш метод тестирования не транзакционный.
Вам нужно аннотировать либо CurriculumPermissionEvaluatorAuthorizationTest
, либо shouldAllowCurriculumRetrieval()
с @Transactional
помощью.
Комментарии:
1. Сэм: Мне очень жаль: я только что понял, что аннотирование метода с помощью @Transactional на самом деле не решило проблему… Моя ошибка в том, что я изначально пометил ответ как принятый…
2. Как выглядит конфигурация транзакции для
curriculumService.findCurriculum(id)
? Аннотирован ли метод, класс или интерфейс с помощью@Transactional
, и если да, то на какоеpropagation
значение они установлены?3.
findCurriculum
@Transactional
соответствует следующему объявлению Roo ITD:declare @type: CurriculumServiceImpl: @Transactional;
Распространение должно быть по умолчанию, т. Е. обязательным.4. Насколько я вижу, ваш метод тестирования все равно должен быть
@Transactional
; в противном случае ваша транзакция (и, следовательно, ваш сеанс) завершается, когда завершается вызовcurriculumService.findCurriculum(id)
.5. Если вы не можете заставить его работать, убедившись, что тест является транзакционным , было бы проще помочь вам, если вы создадите уменьшенный тестовый проект , который демонстрирует проблему.