#EnableBatchProcessing breaks other JPA junit tests - spring-boot

I have a microservice where multiple #DataJpaIntegrationTest junit tests are present. As soon as I am adding #EnableBatchProcessing , all junits annotated with #DataJpaIntegrationTest are failing.
Problem seem to be as no transaction is happening.
I am using custom batch configurer to use Map based JobRepository utilizing Resourceless transactionManager.
#DataJpaIntegrationTest looks as below:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#SpringBootTest
#Transactional
#AutoConfigureDataJpa
#AutoConfigureTestDatabase
#AutoConfigureTestEntityManager
#TestPropertySource(properties = {
"spring.cloud.stream.function.definition="
})
public #interface DataJpaIntegrationTest {
}
Let me know if further details are required
I got the root cause of the problem but unaware of the solution.
As soon as I removed custom batch configurer , everything started working fine.
However, #DataJpaTest is still failing with below reason
"eventSource":"org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer","message":"No transaction manager was provided, using a DataSourceTransactionManager"
How can I have the jpa transaction intact while using map based repository.

Related

Are the project beans already instantiated when we try to run a junit test in spring boot

I am new to Spring and spring boot.
For my spring boot application which is a rest controller, I have some beans along with my data source.
I use my data source to create jdbc template. Now when I am in my rest controller code, I have all these beans #Autowired and they work perfectly fine.
My query is regarding the junit testing part.
When I write my test code inside src/test/java and when I execute my test class within IDE, are the beans defined in my src/main/javacode, instantiated before test case execution?
You might use the same container, or instantiate another container particularly for testing purposes, for which you'll provide a configuration of that other Spring Container separately:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:test-context.xml")
public class SomeClassTest{...}
However, you can also enable support for loading your Application Context and then use the #Autowired fields in your JUnit fixtures, which also works fine too:
#RunWith(SpringRunner.class)
public class SomeTestClass {
....
#Autowired
ApplicationContext context;
....
}
From here, you can get any bean you wish.

Cucumber and Spring boot integration

I have a microservice application developed using spring boot and used cucumber to test. I have a separate project folder "bdd" where I stored all my features files and the step defns and this project is not deployed in the war file.
I have a requirement where I need to hit the DAO class's methods directly for some testing and I found that from BDD folder, I don't have the access to get the instance of the beans from spring boot.
Found some articles as well on how to integrate the cucumber and the spring boot using the #RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) annotations. however It seems not to be working for me.
Does anyone have experience any such requirements or could anyone suggest me on what should be the correct approach.
Thanks.
Edited :
I am trying to use an instance of a bean which was initialized already as part of the spring container. when I tried to #Autowire or #Inject using:here registry is the bean instance I am trying to use.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Component
public class AbstractDefs {
#Autowired
private static ConnectionProviderRegistry registry;
dao = new MyDaoClass(registry);
the variable registry is still null.

Spring Annotation #WebMvcTest does not work in an app that has Jpa repositories

I have a Spring App that uses JPA repositories (CrudRepository interfaces). When I try to test my controller using the new Spring test syntax #WebMvcTest(MyController.class), it fails coz it tries to instantiate one of my service class that uses JPA Repository, does anyone has any clues on how to fix that? The app works when I run it.
Here is the error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.myapp.service.UserServiceImpl required a bean of type 'com.myapp.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.myapp.repository.UserRepository' in your configuration.
According to the doc
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
This annotion only apply on the Spring MVC components.
If you are looking to load your full application configuration and use MockMVC, you should consider #SpringBootTest combined with #AutoConfigureMockMvc rather than this annotation.
I was able to unit test a Rest Controller by implementing junit 5 and using #SpringJUnitConfig along with #WebMvcTest. I am using Spring Boot 2.4.5 and this is my example:
#SpringJUnitConfig
#WebMvcTest(controllers = OrderController.class)
class OrderControllerTest {
#Autowired
private MockMvc mockMvc;
// This is a Mock bean of a Spring Feign client that calls an external Rest Api
#MockBean
private LoginServiceClient loginServiceClient;
// This is a Mock for a class which has several Spring Jpa repositories classes as dependencies
#MockBean
private OrderService orderService;
#DisplayName("should create an order")
#Test
void createOrder() throws Exception {
OrderEntity createdOrder = new OrderEntity("123")
when(orderService.createOrder(any(Order.class))).thenReturn(createdOrder);
mockMvc.perform(post("/api/v1/orders").contentType(MediaType.APPLICATION_JSON).content("{orderId:123}"))
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))TODO: here it will go the correlationId
.andExpect(jsonPath("$.orderId").value("123"));
}
}
Please only use #SpringBootTest when you are implementing integration tests.
I faced this same problem. Using #SpringBootTest and #AutoConfigureMockMvc worked perfectly for me.

Spring-Boot module based integration testing

I have a multi-module Spring-Boot project.
I was wondering how I can set up integration testing just to test Spring Data JPA repositories? The following approach fails with this exception:
HV000183: Unable to load 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath.
Since this module does not depend on the web module, there is no web application that can be started.
#RunWith(SpringJUnit4ClassRunner.class)
#IntegrationTest
#SpringApplicationConfiguration(classes = TestConfiguration.class)
class CardInfoRepositoryIT {
#Autowired CardInfoRepository cardInfoRepository;
#Test
void testLoadData() {
assert cardInfoRepository.findAll().size() == 1
}
}
As Marten mentioned, #IntegrationTest should only be used when you need to test against the deployed Spring Boot application (e.g., deployed in an embedded Tomcat, Jetty, or Undertow container). So if your goal is to test your repository layer in isolation, you should not use #IntegrationTest.
On the other hand, if your tests require specific Spring Boot functionality (in contrast to standard Spring Framework functionality, semantics, and defaults), then you will in fact want to annotate your test class with #SpringApplicationConfiguration instead of #ContextConfiguration. The reason is that #SpringApplicationConfiguration preconfigures the SpringApplicationContextLoader which is specific to Spring Boot.
Furthermore, if you want your repository layer integration tests to run faster (i.e., without the full overhead of Spring Boot), you may choose to exclude configuration classes annotated with #EnableAutoConfiguration since that will auto-configure every candidate for auto-configuration found in the classpath. So, for example, if you just want to have Spring Boot auto-configure an embedded database and Spring Data JPA (with Hibernate as the JPA provider) along with entity scanning, you could compose your test configuration something like this:
#Configuration
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
#EntityScan(basePackageClasses = User.class)
#Import({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class TestRepositoryConfig {}
And then use that configuration in your test class like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestRepositoryConfig.class)
#Transactional
public class UserRepositoryTests { /* ... */ }
Regards,
Sam
p.s. You might find my answer to the following, related question useful as well: Disable security for unit tests with spring boot
I resolved this by having the following test config class.
#Configuration
#EnableAutoConfiguration
#ComponentScan
#PropertySource("classpath:core.properties")
class TestConfiguration {
}
core.properties is also used by the main application and it contains datasource information. #IntegrationTest annotation can be removed on the test class.
I also added the following to the module as dependencies:
testRuntime 'javax.el:javax.el-api:2.2.4'
testRuntime 'org.glassfish.web:javax.el:2.2.4'

Spring JUnit Cannot Run TransactionSynchronizationManager.bindResource, while Normal Spring Env Can

I have a working Spring/Hibernate based web application. Now I need to use Spring JUnit 4 to write an integration test for it.
Here is my test code:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "hibernateTransactionManager", defaultRollback = true)
#ContextConfiguration(locations = {"classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext.xml"})
public class TestXXX extends AbstractTransactionalJUnit4SpringContextTests {
#Test
public void testXXXExecute(){...}
}
With this setting of the test environment, I can access all the beans and use the sessionFactory bean to get data from database.
The problem happens with one test, with calls a production code using TransactionSynchronizationManager to implement two-phase commit.
The code looks like this:
TransactionSynchronizationManager.bindResource(sessionFactoryA, new SessionHolder(sessionA));
TransactionSynchronizationManager.bindResource(sessionFactoryB, new SessionHolder(sessionB));
The code performs well in the dev and production environment, where the full Spring Framework is running. During the JUnit run, the exception is:
[junit] java.lang.IllegalStateException: Already value [org.springframework.orm.hibernate3.SessionHolder#6311e359] for key [org.hibernate.impl.SessionFactoryImpl#56d47236] bound to thread [main]
I cannot use 2 lines of #TransactionConfiguration in the test class to define the two transaction managers that corresponds to the two data sources and two sessionFactory objects. I wonder if AbstractTransactionalJUnit4SpringContextTests cannot duplicate the transaction environment of the real Spring Framework.
Without seeing more of your code, it is difficult to tell exactly what is wrong. In instances where I've seen this error in the past, it was because files named in the #ContextConfiguration included each other. For example, you might have file
applicationContext-bean-cfg.xml
that includes
applicationContext-hibernate-cfg.xml, but then have
#ContextConfiguration(locations = {"classpath:/applicationContext-bean-cfg.xml", "classpath:/applicationContext-hibernate-cfg.xml"}).
The other thing to check is that one of the files doesn't already have a transaction manager defined.

Resources