Initialize Spring Boot test beans before tested Application - java

Given a Spring Boot application that does something on start that needs a mock server:
#SpringBootApplication
public class Application implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.err.println("should be 2nd: application init (needs the mock server)");
}
public boolean doSomething() {
System.err.println("should be 3rd: test execution");
return true;
}
}
Therefore, the mock server should be initialized beforehand:
#SpringBootTest
#TestInstance(Lifecycle.PER_CLASS)
class ApplicationITest {
#Autowired
MockServer mockServer;
#Autowired
Application underTest;
#BeforeAll
void setUpInfrastructure() {
mockServer.init();
}
#Test
void doSomething() {
assertTrue(underTest.doSomething());
}
}
#Component
class MockServer {
void init() {
System.err.println("should be 1st: mock server init");
}
}
But the application seems to be initialized always first:
should be 2nd: application init (needs the mock server)
should be 1st: mock server init
should be 3rd: test execution
How can I get those to be executed in the intended order?
I tried using Order(Ordered.HIGHEST_PRECEDENCE) and #AutoConfigureBefore with no success.

That is not how it works. How would the test be able to call a method on an #Autowired instance without the application context being started? That simply is not how it works, hence the result you see.
However as you just want to call an method after the object has been constructed mark the method with #PostConstruct. With this the init method will be called as soon as the MockServer instance has been created and got everything injected.

Related

How could I take an instance of class in Spring Boot?

It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}

mockito spy does not work on a factory bean method

I have a springboot that I am writing unit test for. There is a factoryBean out of which I get service object in runtime. I want to test that a specific method on this service object is invoked. Here is the app code
#Component
public class AppClient {
#Autowired
ServiceFactory factory
Service secretService
#postContruct
public void init(){
this.secretService=factory.get("secret");
}
public void process(Map<String, Object> param){
for (String key: param.keySet()){
if (key.equals("foobar")){
restService.handle(param.get(key));
}
}
}
}
Here is the unit test I have
#RunWith(SpringRunner.class)
#SpringBootTest
public class AppTest {
#Autowired
AppClient appClient;
#SpyBean
ServiceFactory factory;
Service secretService;
#Before
public void init(){
this.secretService=Mockito.spy(factory.get("secret"));
}
#Test
public void testProcess() {
Object obj = new MyDummyObject();
Map<String, Object> params = new HashMap<>();
params.put("foobar", obj);
appClient.process(params);
Mockito.verify(secretService).handle(obj);
}
}
The test fails and when I run through debugger, I see that handle is invoked. so what is wrong here?
EDIT
#MockBean
ServiceFactory factory;
#Mock
Service secretService
#Before
public void init(){
Mockito.when(factory.get(eq("secret"))).thenReturn(secretService);
}
with this change, factory bean is mocked but secretService is null inside in AppClient. that is, secretService is not being stubbed in. tested through debugger.
The PostConstruct callback executes before the spring application entirely runs and before your test-class make some preparations on the mock of the factory. You can't be able to declare Mockito when().then() expectations on the code which runs in the PostConstruct callback.
I can suggest you make a constructor based injection in the AppClient bean:
#Component
public class AppClient {
private final ServiceFactory factory
#Autowired
public AppClient(ServiceFactory factory){
this.factory = factory;
}
...
}
and test this as a simple unit-test. By manually creating an instance of the AppClient, injecting a mock of the factory, execute the init method and verifying all that you need:
#Test
void initTest(){
when(factory.get(..)).thenReturn(..);
AppClient client = new AppClient(factory);
client.init();
verify(..)
}

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

Using #PostConstruct in a test class causes it to be called more than once

I am writing integration tests to test my endpoints and need to setup a User in the database right after construct so the Spring Security Test annotation #WithUserDetails has a user to collect from the database.
My class setup is like this:
#RunWith(value = SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
public abstract class IntegrationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private Service aService;
#PostConstruct
private void postConstruct() throws UserCreationException {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
}
#Test
public void testA() {
// Some test
}
#Test
public void testB() {
// Some test
}
#Test
public void testC() {
// Some test
}
}
However the #PostConstruct method is called for every annotated #Test, even though we are not instantiating the main class again.
Because we use Spring Security Test (#WithUserDetails) we need the user persisted to the database BEFORE we can use the JUnit annotation #Before. We cannot use #BeforeClass either because we rely on the #Autowired service: aService.
A solution I found would be to use a variable to determine if we have already setup the data (see below) but this feels dirty and that there would be a better way.
#PostConstruct
private void postConstruct() throws UserCreationException {
if (!setupData) {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
setupData = true;
}
}
TLDR : Keep your way for the moment. If later the boolean flag is repeated in multiple test classes create your own TestExecutionListener.
In JUnit, the test class constructor is invoked at each test method executed.
So it makes sense that #PostConstruct be invoked for each test method.
According to JUnit and Spring functioning, your workaround is not bad. Specifically because you do it in the base test class.
As less dirty way, you could annotate your test class with #TestExecutionListeners and provide a custom TestExecutionListener but it seem overkill here as you use it once.
In a context where you don't have/want the base class and you want to add your boolean flag in multiple classes, using a custom TestExecutionListener can make sense.
Here is an example.
Custom TestExecutionListener :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class MyMockUserTestExecutionListener extends AbstractTestExecutionListener{
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
MyService myService = testContext.getApplicationContext().getBean(MyService.class);
// ... do my init
}
}
Test class updated :
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
#TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS,
value=MyMockUserTestExecutionListener.class)
public abstract class IntegrationTests {
...
}
Note that MergeMode.MERGE_WITH_DEFAULTS matters if you want to merge TestExecutionListeners coming from the Spring Boot test class with TestExecutionListeners defined in the #TestExecutionListeners of the current class.
The default value is MergeMode.REPLACE_DEFAULTS.

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.

Categories