How to test #PreDestroy and #Bean methods inside a class - java

I have the following methods inside a class
#Bean(name = "boggle")
public BoggleImpl createBoggleClient() {
BoggleBuilder builder = new BoggleBuilder()
.setRegistryId(getRegistryId())
.setRegistryPassword(getPassword())
return new BoggleFeatureImpl(builder.build());
}
and am using the bean inside a class such as
class A {
private final Boggle boggle;
#PreDestroy
public void destroy() {
if (boggle != null) {
boggle.closeConnection();
}
}
}
Now my code coverage in unit tests show these methods as not covered. Not sure what can i do to cover these methods. Any pointers on the same.

Using SpringJUnit4ClassRunner the ApplicationContext is shared between all the running test cases and the #PreDestroy is called only when the ApplicationContext is closed.
Spring has this behavior because you can be working in a big application which has a slow startup and can be costly to create a new context.
You could annotate your test method with the #DirtiesContext annotation.
From it's javadoc:
Test annotation which indicates that the ApplicationContext associated
with a test is dirty and should therefore be closed and removed from
the context cache.
Use this annotation if a test has modified the
context — for example, by modifying the state of a singleton bean,
modifying the state of an embedded database, etc. Subsequent tests
that request the same context will be supplied a new context.

In this example, class A must be a spring bean. Methods annotated with #PreDestroy are called by the application context when it gets closed. Usually, this happens when the application gets shut down gracefully.
So basically you can "simulate" the situation of closing the application context even from a simple unit test, you don't have to start spring in the test for this.
Treat this method as a regular method with some code regardless of the fact that its called by spring:
class A {
private final Boggle boggle;
public class A(Boggle boggle)
{this.boggle = boggle;}
#PreDestroy
public void destroy() {
if (boggle != null) {
boggle.closeConnection();
}
}
}
Then a test can look like this:
class ATest {
#Test
public void test_boggle_closes_connection_when_the_bean_gets_destroyed() {
// given:
Boggle boggle = Mockito.mock(Boggle.class);
A underTest = new A(boggle);
// when:
underTest.destroy();
// then: verify that boggle closes connection
Mockito.verify(boggle, times(1)).closeConnection();
}
}

Related

How to update row value using JPA repository outside the controller?

I was trying to update the table row data from outside the controller (Inside some threads) and getting 'NullPointerException' always.
Thread code:
public class S3Thread implements Runnable {
#Autowired
private IAutomationService automationService;
#Override
public void run() {
Automation config = new Automation("user1","success");
automationService.updateAutomation(config);
}
}
NullPointer exception thrown on below line:
automationService.updateAutomation(config);
Note: I was able to create/update from the controller class.Only in Thread.
Well, this is the classical Why is my Spring #Autowired field null case. You create the S3Thread instance by yourself, and thus, no beans are injected into it.
Considering you're trying to just do something in a separate thread, you can consider using #Async:
#Async
public void updateAutomationConfiguration() {
Automation config = new Automation("user1", "success");
automationService.updateAutomation(config);
}
Notes:
You have to add the #EnableAsync annotation to any configuration class (eg. your main class) to make this work.
Spring uses proxying by default, which means that you can't add this updateAutomationConfiguration() class to your controller itself. Direct calls to methods within the same bean bypass the proxied logic. The solution is to put this method in a separate bean which can be autowired and invoked from within the controller. I've provided more detailed answers about alternative solutions in this answer.
Spring also has a getting started guide for creating asynchronous methods.
Alternatively, there are also some ways to execute asynchronous calls within controllers, for example by using CompletableFuture within a controller:
#PutMapping("/automation/configuration")
public CompletableFuture<String> updateAutomationConfiguration() {
return CompletableFuture.supplyAsync(() -> {
Automation config = new Automation("user1", "success");
return automationService.updateAutomation(config);
});
}
Related: How to create a non-blocking #RestController webservice in Spring?
Spring does not scan your runnable as it is not annotated with #Component.Try annotating it with #Component/#Service.
Don't forget to set scope required scope though!
There are 2 potential solutions to your problem:
Either you need to make S3Thread class a service by annotating it with #Service or #Component and autowiring it on the calling class, or you can alternatively use the constructor for initializing your automationService, e.g. private IAutomationService automationService = new AutomationService();
Since your thread class is not managed by spring you will not be able to inject the spring managed beans in the S3Thread class.
In order to do that you need to create a class or factory which should be hooked into the spring life cycle.
Once you have the hold of that class you can get the appropriate bean and pass the reference onto/or used in the S3Thread class directly. Something like this
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
{
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
public class S3Thread implements Runnable {
#Override
public void run() {
Automation config = new Automation("user1","success");
IAutomationService automationService=
ApplicationContextUtils.getApplicationContext().getBean(IAutomationService .class);
automationService.updateAutomation(config);
}
}

Intercepting method calls in Java with mockito or proxies

I'd like to know if there is a way to intercept class method call that contacts the server and returns configuration settings for my application and then return required value for my test. My application looks like this
package application;
class Application {
private static synchronized void getServerConfiguration() {
ConfigurationAccessor accessor = new ConfigurationAccessor();
optionOne = accessor.getOption("option-one"); // <- intercept this method call and return different value
...
}
}
In my tests I need to use a running instance of the application to run GUI tests :
package tests;
class SomeTest {
#BeforeClass public static void startApplication() {
createUsers();
Application.start(); <- this will start application and load config from server
}
Unfortunately it's not possible to run connection via proxy server to mock responses.
If you inject the ConfigurationAccessor into the Application class, you would be able to inject a mock in SomeTest. I.e. create a constructor
Application(ConfigurationAccessor accessor) {
this.accessor = accessor;
}
then in your test you would be able to
Application target;
#Mock
ConfigurationAccessor accessorMock;
#Before
public void setUp() {
target = new Application(accessorMock);
}

Make ApplicationContext dirty before and after test class

I have a particular class (let's say MyTest) in my Spring integration tests that is using PowerMock #PrepareForTest annotation on a Spring component: #PrepareForTest(MyComponent.class). This means that PowerMock will load this class with some modifications. The problem is, my #ContextConfiguration is defined on the superclass which is extended by MyTest, and the ApplicationContext is cached between different test classes. Now, if MyTest is run first, it will have the correct PowerMock version of MyComponent, but if not - the test will fail since the context will be loaded for another test (without #PrepareForTest).
So what I want to do is to reload my context before MyTest. I can do that via
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
But what if I also want to reload context after this test is done? So I will have clean MyComponent again without PowerMock modifications. Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
For now I did it with the following hack:
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
on MyTest and then
/**
* Stub test to reload ApplicationContext before execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
#Test
public void aa() {
}
/**
* Stub test to reload ApplicationContext after execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
#Test
public void zz() {
}
I am wondering if there is a prettier way to do that?
As a side question, is it possible to reload only certain bean and not full context?
Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
No, that is unfortunately not supported via #DirtiesContext.
However, what you're really saying is that you want a new ApplicationContext for MyTest that is identical to the context for the parent test class but only lives as long as MyTest. And... you don't want to affect the context cached for the parent test class.
So with that in mind, the following trick should do the job.
#RunWith(SpringJUnit4ClassRunner.class)
// Inherit config from parent and combine with local
// static Config class to create a new context
#ContextConfiguration
#DirtiesContext
public class MyTest extends BaseTests {
#Configuration
static class Config {
// No need to define any actual #Bean methods.
// We only need to add an additional #Configuration
// class so that we get a new ApplicationContext.
}
}
Alternative to #DirtiesContext
If you want to have a context dirtied both before and after a test class, you can implement a custom TestExecutionListener that does exactly that. For example, the following will do the trick.
import org.springframework.core.Ordered;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class DirtyContextBeforeAndAfterClassTestExecutionListener
extends AbstractTestExecutionListener {
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
#Override
public void afterTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
}
You can then use the custom listener in MyTest as follows.
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
#TestExecutionListeners(
listeners = DirtyContextBeforeAndAfterClassTestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MyTest extends BaseTest { /* ... */ }
As a side question, is it possible to reload only certain bean and not full context?
No, that is also not possible.
Regards,
Sam (author of the Spring TestContext Framework)

How to test afterPropertiesSet method in my spring application?

I am working on writing some junit test for my spring application. Below is my application which implements InitializingBean interface,
public class InitializeFramework implements InitializingBean {
#Override
public void afterPropertiesSet() throws Exception {
try {
} catch (Exception e) {
}
}
}
Now I want to call afterPropertiesSet method from my junit test but somehow, I am not able to understand what is the right way to do this? I thought, I can use reflection to call this method but I don't think, it's a right way to do that?
Can anyone provide me a simple example for this on how to write a simple junit test that will test afterPropertiesSet method in InitializeFramework class?
InitializingBean#afterProperties() without any ApplicationContext is just another method to implement and call manually.
#Test
public void afterPropertiesSet() {
InitializeFramework framework = new InitializeFramework();
framework.afterPropertiesSet();
// the internals depend on the implementation
}
Spring's BeanFactory implementations will detect instances in the context that are of type InitializingBean and, after all the properties of the object have been set, call the afterPropertiesSet() method.
You can test that too by having your InitializeFramework bean be constructed by an ApplicationContext implementation.
Say you had
#Configuration
public class MyConfiguration {
#Bean
public InitializeFramework initializeFramework() {
return new InitializeFramework();
}
}
And somewhere in a test (not really junit worthy though, more of an integration test)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
When the context loads you will notice that the afterPropertiesSet() method of the InitializeFramework bean is called.

How to disable #PostConstruct in Spring during Test

Within a Spring Component I have a #PostConstruct statement. Similar to below:
#Singleton
#Component("filelist")
public class FileListService extends BaseService {
private List listOfFiles = new Arrays.list();
//some other functions
#PostConstruct
public void populate () {
for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new String[]{"txt"},true)){
listOfFiles.add(f.getName());
}
}
#Override
public long count() throws DataSourceException {
return listOfFiles.size();
}
// more methods .....
}
During Unit tests I would not like to have the #PostConstruct function called, is there a way to telling Spring not to do post processing? Or is there a better Annotation for calling a initiation method on a class durning non-testing ?
Any of:
Subclass FileListService in your test and override the method to do nothing (as mrembisz says, you would need to place the subclass in package scanned only for tests and mark it as #Primary)
Change FileListService so the list of files is injected by Spring (this is a cleaner design anyway), and in your tests, inject an empty list
Just create it with new FileListService() and inject the dependencies yourself
Boot up Spring using a different configuration file/class, without using annotation configuration.
Since you are not testing FileListService but a depending class, you can mock it for tests. Make a mock version in a separate test package which is scanned only by test context. Mark it with #Primary annotation so it takes precedence over production version.
Declare a bean to override the existing class and make it Primary.
#Bean
#Primary
public FileListService fileListService() {
return mock(FileListService.class);
}
check the profile as this:
#PostConstruct
public void populate () {
if (!Arrays.asList(this.environment.getActiveProfiles()).contains("test")) {
for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new
String[]{"txt"},true)){
listOfFiles.add(f.getName());
}
}
}

Categories