How do I inject mocks into a Spring class marked as "#Transactional"? - spring

I'm using SPring 3.1.1.RELEASE and JUnit 4.8.1. In my test class, I want to mock a private field and discovered the beauty of "ReflectionTestUtils" ...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class OrderServiceTest extends AbstractTransactionalJUnit4SpringContextTests
…
#Autowired
private OrderService m_orderSvc;
#Test
public void testGetPDOrders() throws QuickBaseException, Exception {
…
ReflectionTestUtils.setField(m_orderSvc, "m_orderDao", mockOrderDao);
Below is the class and field I'm trying to mock ...
#Service("orderService")
#Transactional
public class OrderServiceImpl implements OrderService {
…
#Autowired
private OrderDAO m_orderDao;
Disappointingly, I get the below error. I've read that this is because my class is marked as "#Transactional" but I can't find an adequate work-around and it seems like a waste to write setter methods solely to accommodate JUnit. Does anyone have another suggestion about how I can inject my mock object into a private field?
java.lang.IllegalArgumentException: Could not find field [m_orderDao] on target [org.mainco.subco.myclient.service.OrderServiceImpl#282f0e07]
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:107)
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:84)
at org.mainco.subco.myclient.service.OrderServiceTest.testGetPDOrders(OrderServiceTest.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

I'm not sure what mocking library you're using, but there is a helpful integration between Spring and Mockito aptly and clumsily named "Springockito" that can make tactical insertions of mocks in a greater Spring context pretty easy.
The idea is that you change your test application context to map that bean to a mock, rather than try to wire the mock to your parent bean at runtime.
So really you just end up with:
text-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="... http://www.mockito.org/spring/mockito https://bitbucket.org/kubek2k/springockito/raw/tip/springockito/src/main/resources/spring/mockito.xsd">
<mockito:mock id="m_orderDao" class="my.package.OrderDao"/>
<!--... other config ...-->
</beans>
Then you can just autowire the mock itself into your test if you need to interact with it, the same way you are doing for your service as it stands now.
If you're not using Mockito, you can still use the approach above, but instead use your mocking library's method for creating mocks as the factory for the bean.

As of 2014 the easiest solution is to use #InjectMocks annotation that is part of Mockito. This works with any class including the ones marked with #Transactional.
Here is an example :
public class TestTestController {
#Mock
private TestService testService;
#InjectMocks
private TestController testController;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testMocks() throws Exception {
String mockedReturnValue = "this is a mocked reply";
when(testService.getMessage()).thenReturn(mockedReturnValue);
assertEquals(mockedReturnValue, testController.callTestService());
}
}
and the related classes
public class TestController {
#Autowired
private TestService testService;
public String callTestService() {
return testService.getMessage();
}
}
public class TestService {
public static final String THIS_IS_A_TEST = "this is a getMessage";
public String getMessage() {
return THIS_IS_A_TEST;
}
}

Related

Mocking DAO object with MockMvc in Spring Boot

I am new to Integration testing. I have Spring Integration application for which I am writing test cases. here is how I am writing test case:
Activator:
#Service
public class ProdAppActivator {
#Autowired
private ApplicantDAO applicantDAO;
#Transactional
public ApplicantDTO prodApp(ApplicantDTO applicant)
throws TechnicalException, BusinessException {
applicantDAO.updateApp(applicant);
return application;
}
}
Test Class:
public class AcctImplTest extends AbstractTest {
#MockBean
private ApplicantDAO applicantDAO;
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void testProdApp() throws Exception {
ProdAppRequest appRequest = getProdAppRequest();
mvc.perform(post("/baseurl/applicant").headers(getHeaders()).accept(MediaType.APPLICATION_JSON)
.content(mapToJson(appRequest)).contentType(MediaType.APPLICATION_JSON)).andDo(print())
.andExpect(status().isOk());
}
}
AbstractTest:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:group/deposit/svc/product-application-context-test.xml" })
#WebMvcTest
//#IfProfileValue(name ="profiles.active", value ="Integration")
public abstract class AbstractTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
setCMD();
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
I have 2 issues:
This activator has a method with #Transactional annotation. I am not sure why but because of this annotation my test getting failed and giving error as:
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
java.lang.ClassLoader.loadClass(ClassLoader.java:357)
org.springframework.orm.hibernate4.HibernateTransactionManager.isSameConnectionForEntireSession(HibernateTransactionManager.java:711)
org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:445)
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
com.hsbc.group.depositproduct.us.svc.activators.RecordProdAppActivator$$EnhancerBySpringCGLIB$$d1f3f882.recordProdApp(<generated>)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
When I remove this annotation everything works fine. But I don't want to remove this annotation so can some please suggest how to write test here.
if I remove #Transactional annotation; I have used #MockBean to mock DAO object(Interface) and its mocking given object but when I try to return expected value for any DAO call its returning default value. Can some one please suggest how to mock DAO while using #MockBean. I have tried #SpyBean, and other possible ways but didn't get success.
Thanks in advance.

Springboot testing with keycloak

I'm trying to run simple unit test, Keycloak is configured in proper way( I tested it and my mvc app is connecting and users are authenticated_ but now I tried to test my controllers and even though
I used spring slices keycloak adapter is invoked and giving me errors. Adapter configuration is mostly brought from keycloak documentation
#WebMvcTest(UserController.class)
class UserControllerTest {
#MockBean
UserService userService;
#Autowired
MockMvc mockMvc;
#BeforeEach
void setUp() {
}
#AfterEach
void tearDown() {
reset(userService);
}
#Test
void logout() throws Exception {
mockMvc.perform(get("/logout"))
.andExpect(status().isOk());
}
}
but I've got errors when I try to run it, stack trace:
java.lang.NullPointerException
at org.keycloak.adapters.KeycloakDeploymentBuilder.internalBuild(KeycloakDeploymentBuilder.java:57)
at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:205)
at org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver.resolve(KeycloakSpringBootConfigResolver.java:37)
at org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper.resolve(KeycloakSpringConfigResolverWrapper.java:40)
at org.keycloak.adapters.AdapterDeploymentContext.resolveDeployment(AdapterDeploymentContext.java:89)
at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182)
at org.czekalski.userkeycloak.controller.UserControllerTest.logout(UserControllerTest.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
adding to test #TestPropertySource("classpath:secTest.properties") where inside secTest.properties
keycloak.enabled = false didn't help
code under test:
#Controller
public class UserController {
private final UserService userService;
public UserController( UserService userService) {
this.userService = userService;
}
#GetMapping("/index")
public String logout(){
return "users/logout";
}
#GetMapping("/logged")
public String loggedIn(Model model){
model.addAttribute("token", userService.getloggedInUser());
return "users/logged";
}
I just wrote a set of libs to ease unit-testing of secured Spring apps.
It includes a #WithMockKeycloackAuth annotation, along with Keycloak dedicated MockMvc request post-processor and WebTestClient configurer / mutator
Sample usage:
#RunWith(SpringRunner.class)
#WebMvcTest(GreetingController.class)
#ContextConfiguration(classes = GreetingApp.class)
#ComponentScan(basePackageClasses = { KeycloakSecurityComponents.class, KeycloakSpringBootConfigResolver.class })
public class GreetingControllerTests extends ServletUnitTestingSupport {
#MockBean
MessageService messageService;
#Test
#WithMockKeycloackAuth
public void whenUserIsNotGrantedWithAuthorizedPersonelThenSecretRouteIsNotAccessible() throws Exception {
mockMvc().get("/secured-route").andExpect(status().isForbidden());
}
#Test
#WithMockKeycloackAuth("AUTHORIZED_PERSONNEL")
public void whenUserIsGrantedWithAuthorizedPersonelThenSecretRouteIsAccessible() throws Exception {
mockMvc().get("/secured-route").andExpect(content().string(is("secret route")));
}
#Test
#WithMockKeycloakAuth(
authorities = { "USER", "AUTHORIZED_PERSONNEL" },
id = #IdTokenClaims(sub = "42"),
oidc = #OidcStandardClaims(
email = "ch4mp#c4-soft.com",
emailVerified = true,
nickName = "Tonton-Pirate",
preferredUsername = "ch4mpy"),
privateClaims = #ClaimSet(stringClaims = #StringClaim(name = "foo", value = "bar")))
public void whenAuthenticatedWithKeycloakAuthenticationTokenThenCanGreet() throws Exception {
mockMvc().get("/greet")
.andExpect(status().isOk())
.andExpect(content().string(startsWith("Hello ch4mpy! You are granted with ")))
.andExpect(content().string(containsString("AUTHORIZED_PERSONNEL")))
.andExpect(content().string(containsString("USER")));
Depending of how much of the tooling I propose you wish to you might get spring-security-oauth2-test-addons or spring-security-oauth2-test-webmvc-addons from maven-central:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-test-addons</artifactId>
<version>2.3.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
<version>2.3.4</version>
<scope>test</scope>
</dependency>
First is enough if you're only interested in #WithMockKeycloakAuth annotation. Second adds fluent API (MockMvc request post-processor) and other stuff like MockMvc wrapper with default values for content-type & accept headers
Solution for the Keycloak + Spring Security setup during testing is tricky but IMHO the following is only proper solution to test properly set environment. First thighs first we do not want to use security config conditionally as we want to test it also (RolesAllowed, Post and Pre annotations for example). We do not want to create special configuration for test also for the same reason. The way out is the following configuration:
#Configuration #mandatory
#EnableWebSecurity #mandatory
#EnableGlobalMethodSecurity(jsr250Enabled = true) #conditional
#EnableConfigurationProperties(KeycloakSpringBootProperties.class) #mandatory
#Slf4j #conditional
class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(#NotNull HttpSecurity http) throws Exception {
super.configure(http);
...
}
#Bean
public #NotNull KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
The really important is the presence of #EnableConfigurationProperties(KeycloakSpringBootProperties.class). Without it you will get NPE during tests. To you application.yml in the test resources or application-test.yml (the same goes analogically for the properties configuration) add the following:
keycloak:
enabled: false #Keycloak is not needed in full functionality
realm: mock #There is no configuration mock for Keycloak in case of testing. Realm must be set but it is not used
resource: mock #There is no configuration mock for Keycloak in case of testing. Resource must be set but it is not used
auth-server-url: http://mock #There is no configuration mock for Keycloak in case of testing. URL must be set but it is not used
bearer-only: true # Because Keycloak do redirect in case of unauthenticated user which leads to 302 status, we switch to strict Bearer mode
credentials:
secret: mock
With this setting and #WithMockUser annotation your #WebMvcTest will be running in the same security config as production with not errors.
I also found a way to do it, but it's quite ugly way. You can just turn off keycloak for testing purposes. Can be better?
in properties file(my is app-dev.properties) set:
keycloak.enabled = false
In application security config class I set
#ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
public class KeycloakConfiguration extends KeycloakWebSecurityConfigurerAdapter {
I also created separate class with security config but only for testing purposes with these annotations
#Profile("app-dev.properties")
#Configuration
#EnableWebSecurity
public class TestSecConfig extends WebSecurityConfigurerAdapter{}
In integration test of controller
#ActiveProfiles("app-dev.properties")
#WebMvcTest(value = FunController.class)
#Import(TestSecConfig.class)
#TestPropertySource("classpath:app-dev.properties")
class FunControllerIT{}
source:
workaround https://github.com/spring-projects/spring-boot/issues/6514
You could also annotate your Testclass with #AutoConfigureMockMvc(addFilters = false) to disable filters from the application context.
Another angle would be to just test your Controller the same way you would test a Service (without #WebMvcTest annotation). After all we don't really need to test Spring's functionality of routing GETs and POSTs and returning HTTP responses. Spring tested that themselves. We are just concerned with testing the unit of work that we wrote... the guts of the controller functions themselves.
#ExtendWith(UserController.class)
class UserControllerTest {
#Mock
UserService userService;
#Test
void logoutShouldShowUsersLogout() throws Exception {
var result = logout();
assertEquals(result, "users/logout");
}
}
Just a thought.

WebSocketConfigurer and #Scheduled() are not work well in an application

I can either use websocket that configure with WebSocketConfigurer or use #Scheduled() to schedule task without any problem.
However, java does not compile when i use both of them.
#Scheduled() annotation may crash with
org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.scheduleAtFixedRate()
java.lang.IllegalStateException: Unexpected use of scheduler.
at org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.scheduleAtFixedRate(WebSocketConfigurationSupport.java:123)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleFixedRateTask(ScheduledTaskRegistrar.java:462)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleFixedRateTask(ScheduledTaskRegistrar.java:436)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTasks(ScheduledTaskRegistrar.java:357)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:332)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:280)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:211)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:102)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:353)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:887)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
at com.yeadev.JavaSpringBootJARAngularSeed.JavaSpringBootJarAngularSeedApplication.main(JavaSpringBootJarAngularSeedApplication.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
source code for WebSocketConfiguration
#Slf4j
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// added end point,
// eg. ws://localhost:8080/ws
// added WebSocketHandler to /ws
registry.addHandler(new WebSocketHandler(),"/ws");
log.info("added handler for WebSocket.");
}
}
source code for ScheduledTasks
#Slf4j
#Component
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// #Scheduled() annotation can not use with WebSocketConfigurer
#Scheduled(fixedRate=1000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
i use Spring Boot 2.0.0.RELEASE
I ran into the same issue today. It looks as if like Spring web socket creates its own taskScheduler bean which doesn't implement any of the normal operations - it just throws IllegalStateException. This bean is then used for all #Scheduled methods. I was able to resolve this by explicitly creating a task scheduler bean in my ApplicationConfig:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
return taskScheduler;
}

Cannot run Unit Tests against REST layer

I cannot unit test the web layer using jhipster (according to spring gs guides):
#RunWith(SpringRunner.class)
#WebMvcTest
public class FlightControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FlightService flightService;
#Test
public void testCreate() throws Exception {
FlightDto expected = new FlightDto();
ReflectionTestUtils.setField(expected, "id", 1L);
when(flightService.createFlight(any(FlightDto.class))).thenReturn(expected);
FlightDto flightDto = new FlightDto();
flightDto.setNumber("CAI-123400");
this.mockMvc.perform(post("/api/flight")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(flightDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(header().string("Location", endsWith("/api/flight/1")));
}
}
The above Unit test success in case of green-field spring boot project, but fails in case of green-field spring boot-based jhipster project:
When run Unit Test in jhispter project (springboot-with-jhipster) from FlightResource I got:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
at org.springframework.util.Assert.state(Assert.java:70)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOrFindConfigurationClasses(SpringBootTestContextBootstrapper.java:202)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:137)
at org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper.processMergedContextConfiguration(WebMvcTestContextBootstrapper.java:35)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:409)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildDefaultMergedContextConfiguration(AbstractTestContextBootstrapper.java:323)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:277)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:112)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:82)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:120)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:105)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:152)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:143)
at org.springframework.test.context.junit4.SpringRunner.<init>(SpringRunner.java:49)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
I can run the test with this code:
#RunWith(SpringRunner.class)
//#WebMvcTest remove #WebMvcTest
//add SpringBootTest
#SpringBootTest(classes = JhUnittestRestApp.class)
public class FlightResourceTest {
//#Autowired remove anotation
private MockMvc mockMvc;
#MockBean
private FlightService flightService;
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Autowired
private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
#Autowired
private ExceptionTranslator exceptionTranslator;
#Before
public void setup() {
//initialize the bean
MockitoAnnotations.initMocks(this);
final FlightResource flightResource = new FlightResource(flightService);
this.mockMvc = MockMvcBuilders.standaloneSetup(flightResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService())
.setMessageConverters(jacksonMessageConverter).build();
}
#Test
public void testCreate() throws Exception {
FlightDTO expected = new FlightDTO();
ReflectionTestUtils.setField(expected, "id", 1L);
when(flightService.save(any(FlightDTO.class))).thenReturn(expected);
FlightDTO flightDto = new FlightDTO();
flightDto.setNumber("CAI-123400");
//update the url to /api/flights so that the test can pass
this.mockMvc.perform(post("/api/flights")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(flightDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(header().string("Location", endsWith("/api/flights/1")));
}
}
Your FlightControllerTest is working in your springboot-no-jhipster project because the main class of the project is annotated with #SpringBoot According to the documentation of #SpringBoot
The #SpringBootApplication annotation is equivalent to using:
#Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes
Since JHipster needs some more configuration than just the default, JHipster is not using #SpringBootApplication as you can see in your project. This is perfectly OK and works without problems.
On the other hand the error message of your test is saying that it can't detect #SpringBootConfiguration. There are other annotation e.g. #ContextConfiguration or #SpringBootTest that are rocomandet to use for the test. Actually there are some inconsistencies on which annotation in main config class fits with the test annotations see here or here.

Issue injecting #Context in resource class

I'm having difficulties injecting HttpHeaders into my rest service class.
#Path("/account")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class UserServiceImpl implements UserService {
#Context
private HttpHeaders headers;
#Override
#POST #Path("/{username}/")
public User get(#PathParam(value = "username") String username)
throws UnknownUserException {
String requestId = headers.getHeaderString("requestId");
return new User();
}
}
When I try running the application I get the following exception during the spring initialisation:
Caused by: java.lang.IllegalArgumentException: Can not set javax.ws.rs.core.HttpHeaders field com.acme.service.UserServiceImpl.headers to com.sun.proxy.$Proxy50
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:758)
at org.apache.cxf.jaxrs.utils.InjectionUtils$1.run(InjectionUtils.java:192)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectFieldValue(InjectionUtils.java:188)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectContextProxiesAndApplication(InjectionUtils.java:1058)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.injectContexts(JAXRSServerFactoryBean.java:405)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.updateClassResourceProviders(JAXRSServerFactoryBean.java:429)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:162)
A bit of experimenting shows that I get the same error when trying to inject anything. I have an exception mapper that has the http headers injected in do problem what so ever, so I created a custom provider to obtain the headers but get the same problem injecting that in.
I'm pretty sure I must be missing something fundamental here.
I know that I could add what I need out of the headers as params to the method but I can't change the interface.
Adding the context to the operation
#Override
#POST #Path("/{username}/")
public User get(#Context HttpHeaders httpHeaders, #PathParam(value = "username") String username)
Gives me the following error.
java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:181)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:97)
I've discovered that if I drop the implements interface then the application actually starts, however nothing is actually injected in.
I've found a workaround (or possibly the expected solution). It appears that spring is using a proxy based on the interface. I created an intermediate interface and stuck on a setHttpHeaders operation on the interface and annotated the implementation with #Context. All seems fine with that.
public interface MyService {
void doStuff();
}
public interface MyServiceInt extends MyService {
void setHttpHeaders(HttpHeaders headers);
}
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class MyServiceImpl implements MyServiceInt {
private HttpHeaders httpHeaders;
#Context
void setHttpHeaders(HttpHeaders headers) {
this.httpHeaders = httpHeaders;
}
#Override
#POST #Path("/doStuff")
public void doStuff() {
}
}
Would still like to hear of a better solution if anyone knows.

Resources