How to test afterPropertiesSet method in my spring application? - java

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.

Related

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

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();
}
}

Sharing an instance of a class across a spring boot application

I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}

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);
}
}

Unit test spring bean implementation as bean

I have a Spring Boot application, using Spring 4, in this app I have an interface and an implementing class:
interface Sorter{
void start();
List<MailingListMessage> getMessages();
}
and
#Service
public class SorterImpl implements Sorter {
List<MailingListMessage> messages;
#Override
public void start() {
}
#Override
public List<MailingListMessage> getMessages() {
return null;
}
}
What I want to to is test the implementation directly but still as a bean with autowiring and everything.
I want to do that because when I need to assert something that for example relates to messages, I have to make the method public as you can see in the Impl class.
Is there a way in Spring to properly write tests for implementations directly and still have them work as beans?
Thanks.

Unit testing compile-time weaving

I am using the aspectj maven plugin to weave Aspects at compile time. When I run the application, the class with the #Advice annotation is being instantiated just before the first time the advice is called. For example:
#Aspect
public class MyAdviceClass {
public MyAdviceClass() {
System.out.println("creating MyAdviceClass");
}
#Around("execution(* *(..)) && #annotation(timed)")
public Object doBasicProfiling(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
System.out.println("timed annotation called");
return pjp.proceed();
}
}
If I have a method using the #Timed annotation, the "creating MyAdviceClass" will be printed the first time that method is called and "timed annotation called" will be printed every time.
I would like to unit test the functionality of the advice by mocking some components in MyAdviceClass however can not do this because MyAdviceClass is instantiated by AspectJ just in time, not through Spring Beans.
What is the best practice method for unit testing something like this?
I have found the solution and would like to post it for anyone else that encounters this. The trick is using factory-method="aspectOf" in your spring bean definition. So using the example above, I would add this line to my applicationContext.xml
<bean class="com.my.package.MyAdviceClass" factory-method="aspectOf"/>
Any my unit test would look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
public class MyAdviceClassTest {
#Autowired private MyAdviceClass advice;
#Mock private MyExternalResource resource;
#Before
public void setUp() throws Exception {
initMocks(this);
advice.setResource(resource);
}
#Test
public void featureTest() {
// Perform testing
}
}
More details are available here.

Categories