Summary & first problem
I am trying to test my user registration mechanism. When a new user account is created via my REST API, a UserAccountCreatedEvent is stored in the database. A scheduled task checks the database every 5 seconds for new UserAccountCreatedEvents and if one is present, sends an email to the registered user. When running my tests I encounter the problem that the table for the UserAccountCreatedEvent can't be found (see exception below). I used to send the email in a blocking manner in the service method, but I recently switched to this async approach. All my tests worked perfectly for the blocking approach and the only thing I changed for the async approach is to include Awaitility in the test.
2019-04-23 11:24:51.605 ERROR 7968 --- [taskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException:
Table "USER_ACCOUNT_CREATED_EVENT" not found; SQL statement:
select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ? [42102-199]
Full stack trace
Second problem
As if that were not enough, the tests behave completely different when running them in debug mode. When I set a breakpoint in the method that is called by the method which is annotated with #Scheduled, it is invoked several times althogh #Scheduled is configured with a fixedDelayString (fixed delay) of 5000ms. Thanks to logging I can even see that several mails were sent. Still, my test SMTP sever (GreenMail) does not receive any emails. How is this even possible? I've intentionally set the transaction isolation to Isolation.SERIALIZABLE so that it should be impossible (as far as I understand transaction isolation) that two scheduled methods access the same Event from the database.
Third problem
To cap it all, when I rerun the failed tests, THEY WORK. But, there are different exceptions on the console (see below). But still, the app starts and the tests finish successfully. There are different test results depending on if I run all tests vs. only the class vs. only the method vs. rerun failed tests. I don't understand how such an indeterministic behaviour can be possible.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Failed to scan classpath for unlisted entity classes
Caused by: java.nio.channels.ClosedByInterruptException: null
Full stack trace
My code
Test class (UserRegistrationTest)
#ActiveProfiles("test")
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class UserRegistrationTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private Routes routes;
#Autowired
private TestConfig testConfig;
#Resource(name = "validCustomerDTO")
private CustomerDTO validCustomerDTO;
#Resource(name = "validVendorDTO")
private VendorRegistrationDTO validVendorRegistrationDTO;
#Value("${schedule.sendRegistrationConfirmationEmailTaskDelay}")
private Short registrationConfirmationEmailSenderTaskDelay;
private GreenMail smtpServer;
// Setup & tear down
#Before
public void setUp() {
smtpServer = testConfig.getMailServer();
smtpServer.start();
}
#After
public void tearDown() {
smtpServer.stop();
}
// Tests
#Test
public void testCreateCustomerAccount() throws Exception {
mockMvc.perform(
post(routes.getCustomerPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validCustomerDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
#Test
public void testCreateVendorAccount() throws Exception {
mockMvc.perform(
post(routes.getVendorPath())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(validVendorRegistrationDTO)))
.andExpect(status().isCreated());
// When run normally, I get a timeout from the next line
await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());
// Verify correct registration confirmation email was sent
MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
assertThat(receivedMessages).hasSize(1);
// other checks
// ...
}
// Helper methods
private Callable<Boolean> smtpServerReceivedOneEmail() {
return () -> smtpServer.getReceivedMessages().length == 1;
}
// Test configuration
#TestConfiguration
static class TestConfig {
private static final int PORT = 3025;
private static final String HOST = "localhost";
private static final String PROTOCOL = "smtp";
GreenMail getMailServer() {
return new GreenMail(new ServerSetup(PORT, HOST, PROTOCOL));
}
#Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(HOST);
javaMailSender.setPort(PORT);
javaMailSender.setProtocol(PROTOCOL);
javaMailSender.setDefaultEncoding("UTF-8");
return javaMailSender;
}
}
Task scheduler (BusinessTaskScheduler)
#Component
public class BusinessTaskScheduler {
private final RegistrationTask registrationTask;
#Autowired
public BusinessTaskScheduler(RegistrationTask registrationTask) {
this.registrationTask = registrationTask;
}
#Scheduled(fixedDelayString = "${schedule.sendRegistrationConfirmationEmailTaskDelay}")
public void sendRegistrationConfirmationEmail() {
registrationTask.sendRegistrationConfirmationEmail();
}
}
The code that is called by the scheduled method (RegistrationTask)
#Component
#Transactional(isolation = Isolation.SERIALIZABLE)
public class RegistrationTask {
private final EmailHelper emailHelper;
private final EventService eventService;
private final UserRegistrationService userRegistrationService;
#Autowired
public RegistrationTask(EmailHelper emailHelper, EventService eventService, UserRegistrationService userRegistrationService) {
this.emailHelper = emailHelper;
this.eventService = eventService;
this.userRegistrationService = userRegistrationService;
}
public void sendRegistrationConfirmationEmail() {
Optional<UserAccountCreatedEvent> optionalEvent = eventService.getOldestUncompletedUserAccountCreatedEvent();
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
User user = event.getUser();
RegistrationVerificationToken token = userRegistrationService.createRegistrationVerificationTokenForUser(user);
emailHelper.sendRegistrationConfirmationEmail(token);
eventService.completeEvent(event);
}
}
}
The event service (EventServiceImpl)
#Service
#Transactional(isolation = Isolation.SERIALIZABLE)
public class EventServiceImpl implements EventService {
private final ApplicationEventDAO applicationEventDAO;
private final UserAccountCreatedEventDAO userAccountCreatedEventDAO;
#Autowired
public EventServiceImpl(ApplicationEventDAO applicationEventDAO, UserAccountCreatedEventDAO userAccountCreatedEventDAO) {
this.applicationEventDAO = applicationEventDAO;
this.userAccountCreatedEventDAO = userAccountCreatedEventDAO;
}
#Override
public void completeEvent(ApplicationEvent event) {
if (!event.getStatus().equals(COMPLETED) && Objects.isNull(event.getCompletedAt())) {
event.setStatus(COMPLETED);
event.setCompletedAt(LocalDateTime.now());
applicationEventDAO.save(event);
}
}
#Override
public Optional<UserAccountCreatedEvent> getOldestUncompletedUserAccountCreatedEvent() {
Optional<UserAccountCreatedEvent> optionalEvent = userAccountCreatedEventDAO.findFirstByStatusOrderByCreatedAtAsc(NEW);
if (optionalEvent.isPresent()) {
UserAccountCreatedEvent event = optionalEvent.get();
setEventInProcess(event);
return Optional.of(userAccountCreatedEventDAO.save(event));
}
return Optional.empty();
}
#Override
public void publishEvent(ApplicationEvent event) {
applicationEventDAO.save(event);
}
// Helper methods
private void setEventInProcess(ApplicationEvent event) {
event.setStatus(Status.IN_PROCESS);
event.setInProcessSince(LocalDateTime.now());
}
}
The UserAccountCreatedEvent
application.yml
schedule:
sendRegistrationConfirmationEmailTaskDelay: 5000 # delay between tasks in milliseconds
I am new to scheduling with Spring, so any help is greatly appreciated!
I'm developing a simple Spring-based web service userSetting to run on Liberty 8.5.5.9. I'm able to build and deploy the service, and when I start it, it appears to understand its context root (userSetting) but none of its endpoints; I believe I'm missing some essential Spring-based linkage of the parts of the application, but can't figure out which.
The main class of the application looks like:
#SpringBootApplication
#ImportResource("classpath:spring-resource1.xml")
#Import({RESTInterface.class})
public class UserSettingApplication {
private static final LoggerUtils logger = new LoggerUtils( UserSettingApplication.class );
public static void main(String[] args) {
logger.debug( "Entering UserSettingApplication.main()" );
System.out.println( "Entering UserSettingApplication.main()" );
SpringApplication.run(UserSettingApplication.class, args);
logger.debug( "Exiting UserSettingApplication.main()" );
System.out.println( "Exiting UserSettingApplication.main()" );
}
// Dan Vega says: Remember that this going to execute after the
// application context is loaded so you could use it to check if
// certain beans exist or what values of certain properties are.
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
logger.info("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
logger.info(beanName);
}
};
}
}
and the class that defines the endpoints (abbreviated to a single end point) is:
#CrossOrigin
#RestController
#RequestMapping(path = "userSetting")
public class RESTInterface {
...
#RequestMapping(path = "/hello", method = { RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String hello() {
return "Hello, dude!";
}
}
when I try to hit the endpoint http://localhost:9080/userSetting/userSetting/hello, I get the message:
Error 404: java.io.FileNotFoundException: SRVE0190E: File not found: /userSetting/hello
It appears that the RestController has not gotten properly hooked up to the application; in desperation, I tried adding the following line to the main class:
#Import({RESTInterface.class})
but this didn't change anything. Can someone please tell me how to properly connect in the endpoints?
Try hitting http://localhost:9080/userSetting/hello
You are giving 'userSetting' twice in the url
I'm currently writing a REST service with spring boot which should provide a file download, i. e. a client application can download files from the service. A file can be several gigabytes big (sometimes bigger than the main memory), i. e. loading the file into the main memory is not an option. So I need some kind of streaming meachanism when sending a file to the client.
One promising solution on the net (taken from here):
#RestController
// ...
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
#ResponseBody
public FileSystemResource getFile(#PathVariable("file_name") String fileName) {
return new FileSystemResource(myService.getFileFor(fileName));
}
But it does not work. I get:
java.lang.IllegalStateException: Unsupported resource class: class org.springframework.core.io.FileSystemResource
at org.springframework.http.converter.ResourceHttpMessageConverter.readInternal(ResourceHttpMessageConverter.java:100)
I researched but don't know what is causing the issue. I tried other approaches (e. g. Downloading a file from spring controllers) but I get the same error. Does anyone know why the solutions seem to work for others but not for me?
Edit: Here is where I call the REST service (jUnit test case):
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DownloadTest {
#LocalServerPort
private int port;
private RestTemplate template;
private String requestPath;
#Before
public void setUp() throws Exception {
template = new RestTemplate();
requestPath = "http://localhost:" + port + "/files/download";
}
#Test
public void test() {
Resource FileSystemResource =
template.getForObject(requestPath, FileSystemResource.class);
}
}
The code of the RestController is working as expected with a small memory footprint (the file is never loaded to the main memory completely).
The client code that works for me (also with a small memory footprint) can be found here.
i fixed the java.lang.IllegalStateException: Unsupported resource error changing this
Resource FileSystemResource =
template.getForObject(requestPath, FileSystemResource.class);
for this
Resource FileSystemResource =
template.getForObject(requestPath, Resource .class);
I am trying to test spring rest documentation for rest API for our services using spring cucumber jvm but end up with a null pointer exeception when I try to execute the scenario, as the framework is not able to intialize the Junit context.
Error Message:
java.lang.NullPointerException at
org.springframework.restdocs.ManualRestDocumentation.beforeOperation(ManualRestDocumentation.java:90) at
org.springframework.restdocs.JUnitRestDocumentation.beforeOperation(JUnitRestDocumentation.java:76)
Code:
private AppProperties props;
#Before("#rest") public void beforeScenario() {
JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation( "target/generated-snippets" );
System.out.println( "jUnitRestDocumentation " +restDocumentation );
spec = new RequestSpecBuilder().addFilter( documentationConfiguration( restDocumentation ) ).build();
System.out.println( "\n spec init .. " +restDocumentation );
}
Step definition code:
#Given("^create a rest document for VHR API$")
public void create_a_rest_document_for_VHR_API() throws Throwable {
estAssured.given( spec )
.accept( "application/json" )
.filter( document( "vhrdocument" ) ) .when()
.get( props.getVhrrequesturl() + "/vhrData/{vehicleID}", "5VW4T7AU0FM029999" ) .then().log().all();
}
You aren't using JUnitRestDocumentation as it's intended to be used. It's designed to be used as a JUnit rule which means it should be a public field annotated with #Rule:
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
Being a rule means that JUnit will automatically call restDocumentation for each test, allowing Spring REST Docs to set up and tear down the test-specific context. The NullPointerException is occurring because restDocumentation hasn't been called in this way and, therefore, the context hasn't been set up.
You haven't described how you're using Cucumber, but if you're using it's JUnit runner you should be able to fix the problem by declaring restDocumentation as a #Rule-annotated field as shown above. If you're not using its JUnit runner, you may need to use Spring REST Docs' ManualRestDocumentation instead. The Spring REST Docs reference documentation contains a section that describes how to set up your tests when you're not using JUnit.
I had the same problem because I had multiple test class inheriting the class, in which I declared the JUnitRestDocumentation instance. My mistake was that I declared the rule using the #Rule annotation. I should have used #ClassRule and declared the instance as static.
#ClassRule
public static JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
it happened with test SpockFramework, and i added to pom.xml:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-junit4</artifactId>
<scope>test</scope>
</dependency>
I had the same symptoms when migrating from RestAssured 2.x to RestAssured 3.1.1.
The codebase had a way to setup RestAssured in order to avoid repetitive ceremony for every tests :
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
#Before
public void configure_rest_assured() {
RestAssured.port = springServerPort;
RestAssured.config = config().objectMapperConfig(
objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
)
...;
RestAssured.requestSpecification = new RequestSpecBuilder()
.addRequestSpecification(documentationConfiguration(docRule, ...))
...
.build();
}
This was working well, until I migrated to 3.x. The issue was that new RequestSpecBuilder() will append itself to the default static RestAssured.requestSpecification.
The first test passed, but when it finished the rule was disposed (the after part), when the second test started to ran, the Before method was chaining
the specification created for the first test (referencing the disposed rule used by the first test method)
the specification created for the second test (referencing the active rule for second test method)
And so on as new tests are ran.
But when the second test is run RestAssured invoke specification in order, e.g. the number 1, but since it was referencing a disposed rule (the beforeOperation was executed on a null context)
To fix that the code had to clear the previous specifications :
#Before
public void configure_rest_assured() {
RestAssured.port = springServerPort;
RestAssured.config = config().objectMapperConfig(
objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
)
...;
RestAssured.requestSpecification = null; // avoid the builder to acquire previous specs.
RestAssured.requestSpecification = new RequestSpecBuilder()
.addRequestSpecification(documentationConfiguration(docRule, ...))
...
.build();
}
For using cucumber-java-8 with spring rest docs and spring-security the following worked for me.
This is combining #AndyWilkison's answer from above but using the cucumber hooks instead of junit rules.
public class StepDefs implements En {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
private ManualRestDocumentation restDocumentation = new ManualRestDocumentation();
public StepDefs() {
BeforeStep((Scenario scenario) -> {
restDocumentation.beforeTest(AuthenticationStepDefs.class, scenario.getName());
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).apply(documentationConfiguration(restDocumentation)).build();
});
AfterStep((Scenario scenario) -> {
restDocumentation.afterTest();
});
When("create a rest document for VHR API", () -> {
MvcResult result = mockMvc.perform(/*
your normal call here
*/).
.andDo(document("documentation")).
.andReturn();
}
}
}
I have several beans in my Spring context that have state, so I'd like to reset that state before/after unit tests.
My idea was to add a method to a helper class which just goes through all beans in the Spring context, checks for methods that are annotated with #Before or #After and invoke them.
How do I get a list of instantiated beans from the ApplicationContext?
Note: Solutions which simply iterate over all defined beans are useless because I have many lazy beans and some of them must not be instantiated because that would fail for some tests (i.e. I have a beans that need a java.sql.DataSource but the tests work because they don't need that bean).
For example:
public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) {
List<Object> singletons = new ArrayList<Object>();
String[] all = ctx.getBeanDefinitionNames();
ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory();
for (String name : all) {
Object s = clbf.getSingleton(name);
if (s != null)
singletons.add(s);
}
return singletons;
}
I had to improve it a little
#Resource
AbstractApplicationContext context;
#After
public void cleanup() {
resetAllMocks();
}
private void resetAllMocks() {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
for (String name : context.getBeanDefinitionNames()) {
Object bean = beanFactory.getSingleton(name);
if (Mockito.mockingDetails(bean).isMock()) {
Mockito.reset(bean);
}
}
}
I am not sure whether this will help you or not.
You need to create your own annotation eg. MyAnnot.
And place that annotation on the class which you want to get.
And then using following code you might get the instantiated bean.
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class));
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){
System.out.println(beanDefinition.getBeanClassName());
}
This way you can get all the beans having your custom annotation.
applicationContext.getBeanDefinitionNames() does not show the beans which are registered without BeanDefinition instance.
package io.velu.core;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class Core {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Core.class);
String[] singletonNames = context.getDefaultListableBeanFactory().getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
}
}
Console Output
environment
systemProperties
systemEnvironment
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
messageSource
applicationEventMulticaster
lifecycleProcessor
As you can see in the output, environment, systemProperties, systemEnvironment beans will not be shown using context.getBeanDefinitionNames() method.
Spring Boot
For spring boot web applications, all the beans can be listed using the below endpoint.
#RestController
#RequestMapping("/list")
class ExportController {
#Autowired
private ApplicationContext applicationContext;
#GetMapping("/beans")
#ResponseStatus(value = HttpStatus.OK)
String[] registeredBeans() {
return printBeans();
}
private String[] printBeans() {
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
if (autowireCapableBeanFactory instanceof SingletonBeanRegistry) {
String[] singletonNames = ((SingletonBeanRegistry) autowireCapableBeanFactory).getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
return singletonNames;
}
return null;
}
}
[
"autoConfigurationReport",
"springApplicationArguments",
"springBootBanner",
"springBootLoggingSystem",
"environment",
"systemProperties",
"systemEnvironment",
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor",
"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory",
"org.springframework.boot.autoconfigure.condition.BeanTypeRegistry",
"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry",
"propertySourcesPlaceholderConfigurer",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store",
"preserveErrorControllerTargetClassPostProcessor",
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor",
"org.springframework.context.annotation.internalRequiredAnnotationProcessor",
"org.springframework.context.annotation.internalCommonAnnotationProcessor",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor",
"org.springframework.scheduling.annotation.ProxyAsyncConfiguration",
"org.springframework.context.annotation.internalAsyncAnnotationProcessor",
"methodValidationPostProcessor",
"embeddedServletContainerCustomizerBeanPostProcessor",
"errorPageRegistrarBeanPostProcessor",
"messageSource",
"applicationEventMulticaster",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat",
"tomcatEmbeddedServletContainerFactory",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration",
"websocketContainerCustomizer",
"spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties",
"org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration",
"localeCharsetMappingsCustomizer",
"org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration",
"serverProperties",
"duplicateServerPropertiesDetector",
"spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration",
"conventionErrorViewResolver",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration",
"errorPageCustomizer",
"servletContext",
"contextParameters",
"contextAttributes",
"spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties",
"spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties",
"org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration",
"multipartConfigElement",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration",
"dispatcherServlet",
"dispatcherServletRegistration",
"requestContextFilter",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration",
"hiddenHttpMethodFilter",
"httpPutFormContentFilter",
"characterEncodingFilter",
"org.springframework.context.event.internalEventListenerProcessor",
"org.springframework.context.event.internalEventListenerFactory",
"reportGeneratorApplication",
"exportController",
"exportService",
"org.springframework.boot.autoconfigure.AutoConfigurationPackages",
"org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration",
"spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties",
"standardJacksonObjectMapperBuilderCustomizer",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration",
"jsonComponentModule",
"jacksonObjectMapperBuilder",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration",
"jacksonObjectMapper",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration",
"org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration",
"defaultValidator",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration",
"error",
"beanNameViewResolver",
"errorAttributes",
"basicErrorController",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter",
"mvcContentNegotiationManager",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration",
"stringHttpMessageConverter",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration",
"mappingJackson2HttpMessageConverter",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration",
"messageConverters",
"mvcConversionService",
"mvcValidator",
"requestMappingHandlerAdapter",
"mvcResourceUrlProvider",
"requestMappingHandlerMapping",
"mvcPathMatcher",
"mvcUrlPathHelper",
"viewControllerHandlerMapping",
"beanNameHandlerMapping",
"resourceHandlerMapping",
"defaultServletHandlerMapping",
"mvcUriComponentsContributor",
"httpRequestHandlerAdapter",
"simpleControllerHandlerAdapter",
"handlerExceptionResolver",
"mvcViewResolver",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration",
"faviconRequestHandler",
"faviconHandlerMapping",
"defaultViewResolver",
"viewResolver",
"welcomePageHandlerMapping",
"org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration",
"objectNamingStrategy",
"mbeanServer",
"mbeanExporter",
"org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration",
"springApplicationAdminRegistrar",
"org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration",
"spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties",
"org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration",
"multipartResolver",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration",
"restTemplateBuilder",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration",
"spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration",
"fileSystemWatcherFactory",
"classPathRestartStrategy",
"classPathFileSystemWatcher",
"hateoasObjenesisCacheDisabler",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration$LiveReloadServerConfiguration",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration",
"optionalLiveReloadServer",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration",
"lifecycleProcessor"
]
I've created a gist ApplicationContextAwareTestBase.
This helper class does two things:
It sets all internal fields to null. This allows Java to free memory that isn't used anymore. It's less useful with Spring (the Spring context still keeps references to all the beans), though.
It tries to find all methods annotated with #After in all beans in the context and invokes them after the test.
That way, you can easily reset state of your singletons / mocks without having to destroy / refresh the context.
Example: You have a mock DAO:
public void MockDao implements IDao {
private Map<Long, Foo> database = Maps.newHashMap();
#Override
public Foo byId( Long id ) { return database.get( id ) );
#Override
public void save( Foo foo ) { database.put( foo.getId(), foo ); }
#After
public void reset() { database.clear(); }
}
The annotation will make sure reset() will be called after each unit test to clean up the internal state.
Using the previous answers, I've updated this to use Java 8 Streams API:
#Inject
private ApplicationContext applicationContext;
#Before
public void resetMocks() {
ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory();
Stream.of(applicationContext.getBeanDefinitionNames())
.map(n -> beanFactory.getSingleton(n))
// My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say
// .map(ConfigurableListableBeanFactory::getSingleton)
.filter(b -> Mockito.mockingDetails(b).isMock())
.forEach(Mockito::reset);
}