Spring Controller Integration Testing using TestNg - java

I am new to Spring unit Testing. Writing test cases for the Spring rest Controller. When I am writing the test cases in this way
#WebAppConfiguration
#ContextConfiguration(locations = {"file:src/test/resources/applicationContext.xml"})
public class TaskControllerIntegrationTest extends AbstractTransactionalTestNGSpringContextTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#BeforeClass
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void testGetAllTasks() throws Exception {
mockMvc.perform(get("/v1/testSessions/{testSessionId}/tasks", 1l))
.andExpect(status().isOk());
}
}
I am getting the testcase failed don't know why :( while If i initialize the MockMvc using the standalone controller it works perfect and my allTest cases are passes.The setup for the MockMvc using standalone controller is in this way. It's for unit testing:
#Mock
private TaskService taskServiceMock;
#InjectMocks
private TaskController taskController;
private MockMvc mockMvc;
#Spy
List<Task> allTasks = new ArrayList<Task>();
/* #Spy
List<TaskSessionModel> taskSessionModelList = new ArrayList<TaskSessionModel>();*/
#BeforeClass
public void setUp() {
// Mockito.reset(taskServiceMock);
MockitoAnnotations.initMocks(this);
// taskSessionModelList = getAllTaskSessionModels();
allTasks = getAllTasks();
this.mockMvc = MockMvcBuilders.standaloneSetup(taskController).build();
}
#Test
public void testGetAllTasks() throws Exception {
// when(taskSessionDao.findAllByTestSession(1l)).thenReturn(taskSessionModelList);
when(taskServiceMock.getAllTasks(1l)).thenReturn(allTasks);
mockMvc.perform(get("/v1/testSessions/{testSessionId}/tasks", 1l)
.accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].name", is("Average")));
List<Task> expTaskList = taskController.getAllTasks(1l);
verify(taskServiceMock, times(2)).getAllTasks(1l);
verifyNoMoreInteractions(taskServiceMock);
assertEquals(allTasks, expTaskList);
}
But if i write the same unit test by using the WebApplicationContext .. I will get error and test cases will failed :
java.lang.AssertionError: Status
Expected :200
Actual :500
<Click to see difference>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:60)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:89)
at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:654)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:152)
at com.blueoptima.dt.controller.TaskControllerIntegrationTest.testGetAllTasks(TaskControllerIntegrationTest.java:46)
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.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.MethodInvocationHelper$1.runTestMethod(MethodInvocationHelper.java:200)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:171)
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.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:212)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:707)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
at org.testng.SuiteRunner.run(SuiteRunner.java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
at org.testng.TestNG.run(TestNG.java:1057)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:74)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:124)
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 com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
I don't know what's the wrong? I tried almost all the solution for the WebApplicationContext setup(see the first code) it's in the same way as I see .. on this website and mostly configuration in spring official website and other after googling it but still error remains same?
Have a look into this ! Cheers !!

Go back one step, forget about all Spring and technology stuff and ask yourself what you want to achieve with the tests?
If you want to do a real integration tests, don't mock controllers or services, so the following code makes no sense:
#Mock
private TaskService taskServiceMock;
That should be the problem in your setup. Remove all mocks instead of MockMvc and try again. Take a cup of coffee and read the official documentation, it will teach you all the details including setting up a in-memory-database and setting up the Spring TestContext Framework.
To access your controllers, you have two choices, depending on how deep the tests shall go.

Related

TestNG #BeforeClass initialization code not running before Test

I am writing test codes with Selenium, TestNG. I am trying to set some elements on the #BeforeClass section as I am repeating those actions on the same page. But I am getting null pointer exception. Is there a way to fix this issue?
public class RunTest {
private static WebElement user;
private static WebElement pass;
private static WebElement login;
#BeforeClass
public static void driveraBaglan() {
//Driver is declared here. I removed it to give the simple code.
user = driver.findElement(By.id("user"));
pass = driver.findElement(By.id("pass"));
login = driver.findElement(By.xpath("//button[contains(.,'Log In')]"));
statusMessage = driver.findElement(By.id("login-status-message")).getText();
}
#Test(priority=1)
public void loginNoInfo() {
user.clear();
pass.clear();
}
I get the null pointer error on user.clear() , pass.clear() and login.click() functions. All tests on the page runs on the same page. I don't want to use repeated "find element by id" tags on each test on the page.
#BeforeTest: The annotated method will be run before any test method belonging to the classes inside the <test> tag is run.
#BeforeClass: The annotated method will be run before the first test method in the current class is invoked
try to change the annotation
#BeforeTest
public static void driveraBaglan() {
//Driver is declared here. I removed it to give the simple code.
user = driver.findElement(By.id("user"));
pass = driver.findElement(By.id("pass"));
login = driver.findElement(By.xpath("//button[contains(.,'Log In')]"));
statusMessage = driver.findElement(By.id("login-status-message")).getText();
}
I never used TestNG but I do use Junit. Trying to replicate your example, I obtained the following exception:
[TestNG] Running:
/Users/davide.patti/Library/Caches/IntelliJIdea2017.3/temp-testng-customsuite.xml
java.lang.NullPointerException
at TestSO.loginNoInfo(TestSO.java:27)
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.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
at org.testng.SuiteRunner.run(SuiteRunner.java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1198)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1123)
at org.testng.TestNG.run(TestNG.java:1031)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
The reason was because, in my imported classes I was using the BeforeClass of Junit:
import org.junit.BeforeClass;
import org.testng.annotations.Test;
and not of TestNG.
If your null pointer exception is the same, be sure that your imported classes are the correct ones:
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
I got this same issue by an accidental change I've done.
You may also get the null pointer exception on objects you are initializing in #BeforeClass or #BeforeTest, when you have a dilemma situation as below (where the class is set to as a disabled test class while having enabled test methods).
#Test(enabled=false)
public class TestClass{
A a;
#BeforeClass
public void doBeforeClass(){
a=new A();
//
}
#Test
public void test1(){
a.callMethod1();
}
#Test
public void test2(){
a.callMethod2();
}
}
Here, when you are running full-suite by putting #Test(enabled=false) only for the class, still the suite will run individual tests in the class since #Test annotations are present there. However, since #BeforeClass is not running for whole class, object 'a' is not initialized and will cause null pointer exceptions.
In such cases, the individual #Test annotations too need to be changed to #Test(enabled=false)

Autowiring ApplicationListener as null

Dear StackOverflow experts.
We are solving interesting issue in current sprint and our understanding of Spring workflow doesn't allow us to find a solution.
So we ask you to share your thought on next problem:
We need to import class from a library which we can not modify right now.
The thing is that the class we need to import has an Autowired dependency of a class which implements "ApplicationListener".
We won't use this dependency in our code (it is used in methods we won't call).
We trying to set all unused dependencies to null.
But when we set this class to null we get an exception:
Caused by: java.lang.IllegalArgumentException: Delegate listener must not be null
Obviously, we don't want to instantiate ApplicationListener class in our application.
To reproduce our situation we create a simple unit tests:
Application configuration:
#Configuration
public class TestApplicationConfiguration {
#Bean
public ApplicationListener<ContextRefreshedEvent> applicationListener() {
return null;
}
}
Test class:
#ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ConfigurationManagerTests extends AbstractTestNGSpringContextTests {
#Autowired
private ApplicationListener<ContextRefreshedEvent> applicationListener;
#Test
public void dummyTest() {
Assert.assertNull(applicationListener);
}
}
Full stacktrace of the exception listed below.
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.springTestContextPrepareTestInstance(AbstractTestNGSpringContextTests.java:149)
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.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:86)
at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:514)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:215)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:142)
at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:178)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
at org.testng.TestRunner.privateRun(TestRunner.java:782)
at org.testng.TestRunner.run(TestRunner.java:632)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:366)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:361)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:319)
at org.testng.SuiteRunner.run(SuiteRunner.java:268)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1244)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
at org.testng.TestNG.run(TestNG.java:1064)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:124)
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 com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.IllegalArgumentException: Delegate listener must not be null
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.context.event.GenericApplicationListenerAdapter.<init>(GenericApplicationListenerAdapter.java:48)
at org.springframework.context.event.AbstractApplicationEventMulticaster.supportsEvent(AbstractApplicationEventMulticaster.java:280)
at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:229)
at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:185)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:382)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:336)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:108)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
... 33 more
Thanks.
Looks like Spring itself doesn't allow that bean to be null, so why not just create a dummy one which doesn't do anything?
#Bean
public ApplicationListener<ContextRefreshedEvent> applicationListener() {
return new ApplicationListener<ContextRefreshedEvent>() {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Do nothing
}
};
}

Mockito Exception when mocking

When I try to run the following test
public class FavoriteServiceTest extends AbstractCoreTest {
#Autowired
private FavoriteRepository favoriteRepository;
#Autowired
private RevisionService revisionService;
#Autowired
private FavoriteService favoriteService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(revisionService.getGlobalRevisionNumber()).thenReturn(1L);
}
#Test
public void loadFavorites() throws Exception {
when(favoriteRepository.findFavoritesByUserId("123")).thenReturn(Collections.emptyList());
List<Favorite> favorites = favoriteService.loadFavorites(123L);
assertThat(favorites.size(), is(0));
}
I get the following exception, but im pretty sure the mock is correct initialized
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods
declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
at FavoriteServiceTest.setUp(FavoriteServiceTest.java:44) 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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
Just replace #Autowired with #Mock -.-
#Mock
private DocumentService documentService;
Your favoriteRepository should be a mock object, benfinit from spring boot, you can using #MockBean here.

Issue with #WithUserDetails and spring boot 1.4 TestEntityManager

I have an issue with Spring Boot's TestEntityManager and #WithUserDetails annotation.
Here is my test suite:
public class AdvertisementAuthorizationTest extends AbstractIntegrationTest {
private static final String IMPERSONATOR_EMAIL = "joe.hacker#gmail.com";
private final static String OWNER_OF_ADVERTISEMENT_EMAIL = "john.nice#gmail.com";
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private MockMvc mockMvc;
private Advertisement advertisement;
private UserAccount impersonator;
private ObjectMapper mapper = new ObjectMapper();
#Before
public void setUp() {
advertisement = testEntityManager.persist(createAdvertisement(OWNER_OF_ADVERTISEMENT_EMAIL));
impersonator = testEntityManager.persist(createUserAccount(IMPERSONATOR_EMAIL));
}
#Test
#WithUserDetails(IMPERSONATOR_EMAIL)
public void shouldNotAllowAdvertisementModification() throws Exception {
String jsonAdvertisement = mapper.writeValueAsString(advertisement);
mockMvc.perform(put("/api/advertisement/{id}", advertisement.getId())//
.contentType(MediaType.APPLICATION_JSON)//
.content(jsonAdvertisement))//
.andDo(print())//
.andExpect(status().isForbidden());//
}
#Test
#WithUserDetails(OWNER_OF_ADVERTISEMENT_EMAIL)
public void shouldAllowAdvertisementModification() throws Exception {
String jsonAdvertisement = mapper.writeValueAsString(advertisement);
mockMvc.perform(put("/api/advertisement/{id}", advertisement.getId())//
.contentType(MediaType.APPLICATION_JSON)//
.content(jsonAdvertisement))//
.andDo(print())//
.andExpect(status().isOk());//
}
}
Here is the super class:
#AutoConfigureMockMvc
#AutoConfigureTestEntityManager
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {TestApplication.class})
#WebAppConfiguration
#ActiveProfiles(Profiles.TEST)
#Transactional
public abstract class AbstractIntegrationTest {
}
When I run the tests it seems the entities are not persisted to H2 by TestEntityManager as indicated by this error message:
Hibernate: select useraccoun0_.id as id1_9_, useraccoun0_.address_id as address13_9_, useraccoun0_.email as email2_9_, useraccoun0_.email_notification as email_no3_9_, useraccoun0_.enabled as enabled4_9_, useraccoun0_.first_name as first_na5_9_, useraccoun0_.last_connection_date as last_con6_9_, useraccoun0_.password as password7_9_, useraccoun0_.registration_date as registra8_9_, useraccoun0_.role as role9_9_, useraccoun0_.token as token10_9_, useraccoun0_.user_account_type as user_ac11_9_, useraccoun0_.version as version12_9_ from user_account useraccoun0_ where useraccoun0_.email=?
22:52:39.943 [Test worker] WARN o.s.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener#43ec4dcd] to process 'before' execution of test method [public void com.bignibou.it.web.security.advertisement.AdvertisementAuthorizationTest.shouldNotAllowAdvertisementModification() throws java.lang.Exception] for test instance [com.bignibou.it.web.security.advertisement.AdvertisementAuthorizationTest#646496bc]
java.lang.IllegalStateException: Unable to create SecurityContext using #org.springframework.security.test.context.support.WithUserDetails(value=joe.hacker#gmail.com, userDetailsServiceBeanName=)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createSecurityContext(WithSecurityContextTestExecutionListener.java:79)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.beforeTestMethod(WithSecurityContextTestExecutionListener.java:56)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:269)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:112)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:56)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
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:497)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
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:497)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:364)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.security.core.userdetails.UsernameNotFoundException: Username: joe.hacker#gmail.com not found!
at com.bignibou.service.security.UserAccountUserDetailsService.loadUserByUsername(UserAccountUserDetailsService.java:21)
at org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory.createSecurityContext(WithUserDetailsSecurityContextFactory.java:56)
at org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory.createSecurityContext(WithUserDetailsSecurityContextFactory.java:39)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createSecurityContext(WithSecurityContextTestExecutionListener.java:76)
... 43 common frames omitted
Can someone please help?
This is an issue with timing with regard to TestExecutionListener callbacks and #Before test methods.
#WithUserDetails is supported by the Spring Security's WithSecurityContextTestExecutionListener which will never run after a #Before method. It is therefore impossible for Spring Security to see the user that you persist to the database in your setUp() method. That's basically what the exception is telling you: Spring Security attempted to read the user from the database before it existed.
One way to fix this is to migrate to #Sql support for inserting test data in the database. You might not find that as comfortable as simply persisting your entities, but the #Sql approach allows the test data to be created within the test-managed transaction (i.e., does not require manual clean up). Note that you will have to upgrade to Spring Security 4.1.1 in order for this to work properly.
An alternative way to address this is to persist your entities in a user-managed transaction in a #BeforeTransaction method -- for example, using Spring's TransactionTemplate. However, you will then need to manually clean up the database in an #AfterTransaction method in a similar fashion. Plus, you will still need to upgrade to Spring Security 4.1.1 in order for this to work.
Something like the following should do the trick:
#Autowired
private TestEntityManager testEntityManager;
#Autowired
PlatformTransactionManager transactionManager;
#BeforeTransaction
public void setUp() {
new TransactionTemplate(transactionManager).execute(status -> {
UserAccount owner = testEntityManager.persist(createUserAccount(OWNER_OF_ADVERTISEMENT_EMAIL));
Language language = testEntityManager.persist(createLanguage("Français"));
DayToTimeSlot dayToTimeSlot = testEntityManager.persist(createDayToTimeSlot());
advertisement = testEntityManager.persist(createAdvertisement(owner, language, dayToTimeSlot));
impersonator = testEntityManager.persist(createUserAccount(IMPERSONATOR_EMAIL));
return null;
});
}
#AfterTransaction
public void tearDown() {
new TransactionTemplate(transactionManager).execute(status -> {
testEntityManager.remove(testEntityManager.find(Advertisement.class, advertisement.getId()));
UserAccount owner = advertisement.getUserAccount();
testEntityManager.remove(testEntityManager.find(UserAccount.class, owner.getId()));
testEntityManager.remove(testEntityManager.find(UserAccount.class, impersonator.getId()));
return null;
});
}
Regards,
Sam

Vaadin Spring Addon NotSerializableException for XmlWebApplicationContext even with transient ApplicationContext UI's member

I am integrating Spring into Vaadin and thanks to the Vaadin Spring addon and to this wiki: https://vaadin.com/wiki?p_p_id=36&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&p_p_col_id=row-1&p_p_col_pos=2&p_p_col_count=4&_36_struts_action=%2Fwiki%2Fview&p_r_p_185834411_nodeName=vaadin.com+wiki&p_r_p_185834411_title=I+-+Getting+started+with+Vaadin+Spring I was actually able to do it.
However when I restart Tomcat and the Vaadin application is restarted I get the following NotSerializableException complaining about Spring's XmlWebApplicationContext:
SEVERE: Exception loading sessions from persistent storage
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: org.springframework.web.context.support.XmlWebApplicationContext
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1355)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1993)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1918)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at java.util.LinkedList.readObject(LinkedList.java:1149)
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 java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1896)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1993)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:501)
at com.vaadin.server.VaadinSession.readObject(VaadinSession.java:1443)
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 java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1896)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at org.apache.catalina.session.StandardSession.doReadObject(StandardSession.java:1634)
at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:1099)
at org.apache.catalina.session.StandardManager.doLoad(StandardManager.java:261)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:180)
at org.apache.catalina.session.StandardManager.startInternal(StandardManager.java:460)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5213)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.NotSerializableException: org.springframework.web.context.support.XmlWebApplicationContext
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at java.util.LinkedList.writeObject(LinkedList.java:1131)
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 java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.apache.catalina.session.StandardSession.doWriteObject(StandardSession.java:1710)
at org.apache.catalina.session.StandardSession.writeObjectData(StandardSession.java:1116)
at org.apache.catalina.session.StandardManager.doUnload(StandardManager.java:401)
at org.apache.catalina.session.StandardManager.unload(StandardManager.java:320)
at org.apache.catalina.session.StandardManager.stopInternal(StandardManager.java:487)
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5409)
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1425)
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1414)
... 4 more
Actually the code is really simple (based on the tutorial and some other Spring features):
Here is the UI class:
package com.example.vaadinwithspring;
//...
#SpringUI("")
#Theme("valo")
#SuppressWarnings("serial")
public class MyUI extends UI {
private transient ApplicationContext appContext;
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
UserService service = getUserService(vaadinRequest);
User user = service.getUser();
String userName = user.getName();
Label userHelloLabel = new Label("Hello " + userName + "!");
layout.addComponent(userHelloLabel);
}
#WebListener
public static class MyContextLoaderListener extends ContextLoaderListener {
}
#Configuration
#EnableVaadin
public static class MyConfiguration {
#Bean(name="userService")
public UserService helloWorld() {
return new UserServiceImpl();
}
}
#WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends SpringAwareVaadinServlet {
}
private UserService getUserService(VaadinRequest request) {
WrappedSession session = request.getWrappedSession();
HttpSession httpSession = ((WrappedHttpSession) session).getHttpSession();
ServletContext servletContext = httpSession.getServletContext();
appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
return (UserService) appContext.getBean("userService");
}
}
Although quite messy (I followed the wiki), the only important things here are the Vaadin Spring annotations (#SpringUI(""), #EnableVaadin, as for the linked tutorial) and some Spring's annotations (beyond the #Configuration one I also define a Spring bean named "userService" via the #Bean annotation inside the MyConfiguration class). Please also note that I have a member transient field which references Spring's ApplicationContext (which I use inside the UI's getUserService(VaadinRequest) method in order to get the "userService" bean).
Here is the code either for the UserService interface and for the class UserServiceImpl:
package com.example.vaadinwithspring;
public class UserService {
public User getUser();
}
-------------------------------------------------------------------
package com.example.vaadinwithspring;
public class UserServiceImpl implements UserService {
#Override
public User getUser() {
User user = new User();
user.setName("UserName");
return user;
}
}
And then, the User class:
package com.example.vaadinwithspring;
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
All components are dummy implementations, just to see that Vaadin cooperates fine with Spring and that everything works.
Finally, the Vaadin Spring tutorial advises to use an applicationContext.xml and put it inside src/main/webapp/WEB-INF/ when not using Spring Boot or JavaConfig.
I have created the following applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<bean class="com.example.vaadinwithspring.MyUI.MyConfiguration" />
<context:component-scan base-package="com.example.vaadinwithspring" />
</beans>
Now, as I said, if I launch the application, everything works great. The only issue I am facing is that NotSerializableException related to XmlWebApplicationContext when I restart the application.
Why the exception is thrown at all if I have a transient ApplicationContext member which shouldn't be serialized? I suppose that Spring uses another instance of XmlWebApplicationContext and uses it internally, is it so? Then:
Is there a way to fix it? How?
Thanks for the attention!
By default, Tomcat tries to load sessions from previous run on restart. If it fails loading those sessions (your case), it prints the error to log then it initializes your application normally. Effectively, there is no problem here but you don't get session persistence. Unless you're using a production app, you don't need this functionality.
Some approaches you may take other than simply ignoring it:
Disable session persistence
Use another container like Jetty, which does not persist sessions by default
I believe the problem comes from SpringAwareVaadinServlet. It injects your applicationContext into the SpringAwareUIProvider and it effectively gets stored in persistent session store.
If you really want session persistence with this setup, you need to extend VaadinServlet and implement a custom UIProvider that does not store applicationContext itself but it gets it from another place, possibly a static field in your servlet.

Categories