Spring Boot: Use Hibernate Session in a unit test - java

In my Spring Boot application I access my Hibernate session as shown in this answer: https://stackoverflow.com/a/33881946/272180
Now I also want to access the Hibernate Session in a unit test.
How can I set up the datasource and access the Hibernate Session in a unit test of a Spring Boot application?
When I simply Autowire it, I get org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread.
Autowiring and using a #Service works flawlessly.
My unit-testing class looks like this atm:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebIntegrationTest
public class WebTest {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private UserService userService;
#Test
public void testService() {
final List<User> users = userService.getUsers();
// do something with users
// ...
}
}
App.class refers to the class with the main method which is used to run the Spring Boot application.

In fact, the solution was as easy as adding #Transactional to the test-class.
After that I could use the SessionFactory as usual.

Related

What is the cause of LazyInitializationException when testing Spring Boot web app with MockMvc and AutoConfigureMockMvc

I am trying out a few examples of unit testing Spring Boot MVC from this tutorial - https://spring.io/guides/gs/testing-web/. My project has Spring Boot MVC and JPA (https://github.com/shankarps/SfgUdemyRecipes)
The Controller that is tested retrieves a list of Recipe entities which have many-to-many mapping with Category entities.
#RequestMapping({"", "/", "/index"})
public String getIndexPage(Model model){
log.info("Getting all recipes");
model.addAttribute("recipes", recipeService.getAllRecipes());
return "index";
}
This Controller works when the Spring Boot app is launched. The list of recipes are shown in the browser.
This test case with SpringBootTest and TestRestTemplate works as expected.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IndexControllerHttpTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testIndexUrl(){
String response = this.restTemplate.getForObject("http://localhost:"+port+"/", String.class);
}
}
However, when I run a test case with MockMVC to test the Controller as a MVC object (with #AutoConfigureMockMvc), without the Http server, I get org.hibernate.LazyInitializationException Exception.
#SpringBootTest
#AutoConfigureMockMvc
public class IndexControllerMockMVCTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
}
Here is the exception stack trace in full:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.shankar.sfg.recipes.recipes.domain.Category.recipes, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentSet.toString(PersistentSet.java:327)
at java.lang.String.valueOf(String.java:2994)
This looks like an issue with transaction scope. Can anyone help find the root cause? What additional configuration is needed to test the Spring application context when tested as a complete Web application (with #SpringBootTest), versus testing at the MVC layer (#SpringBootTest and #AutoConfigureMockMvc)?
The solution was to add #Transactional to the Test method that called the Mock MVC.
#Test
#Transactional
public void testIndexControllerView() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(status().isOk());
}
This answer deals with similar issue for unit testing a JPA repository object.
LazyInitializationException: could not initialize proxy - no Session
I am not yet sure why a transaction scope is needed when testing the controller without the HTTP server.

Spring Boot trying to create mongo repositories in tests using #JsonTest annotations

I have an application using SpringBoot2 with mongodb and I am trying to test json serialization of some DTOS by making tests like:
#JsonTest
#RunWith(SpringRunner.class)
public class SomeDTOTest {
#Autowired
JacksonTester < SomeDTO > json;
#Test
public void someTest() {}
}
However underneath spring is trying to create repository bean and giving me informations:
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean named 'mongoTemplate' that could not be found.
Action:
Consider defining a bean named 'mongoTemplate' in your configuration.
I have more integration test that is using the repository and are annotated with #SpringBootTests and they are working fine...
Is there a way of restricting spring to only creating JacksonTester bean?
You could just create a test without spring runner.
This is an example example test
When loading the spring context if there is an autowired annotation of a mongotemplate somewhere spring will try to provide it. You might consider:
Provided mongo template in tests
Try using #DataMongoTest which will provide an embedded database.
Set an Autowired not required
Use #Autowired(required= false)
Mock mongotemplate
Use #MockBean annotation in order to mock mongoTemplate
I found it quite challenging to have both Integration tests as well as Unit tests in a Spring Boot application.
I checked Spring website and tried many solutions. The one that worked for me was to exclude the AutoConfiguration classes:
#RunWith(SpringRunner.class)
#JsonTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class JsonTests {
#Autowired
private JacksonTester json;
#MockBean
private MyRepository repository;
#MockBean
private MongoTemplate mongoTemplate;
#Test
public void someTest() {}
}
You can find a complete Spring Boot application that include Integration and Unit tests here.

Spring Boot Exclude DataSource Configuration

I have a small application that when live, makes a database connection, and stores and persists some data.
I'm currently in the midsts of trying to write some tests, and I'm wanting to completely cut off the database part of the application, and just mock it in the tests.
The Datasource is setup with a configuration class.
#Component
#Configuration
public class DataSourceConfiguration {
#Bean
public DataSource myDataSource() { ... }
}
and a test boostrap that currently looks similar to
#RunWith(SpringRunner.class)
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class MyTest {
}
When running the test, I can see that Spring is trying to instantiate Hibernate, and a connection to the db, I assume because of my config class. How can I stop Spring from doing this?
No Need to use : #EnableAutoConfiguration
We can narrow down the tests to just the web layer by using #WebMvcTest as below,
#RunWith(SpringRunner.class)
#WebMvcTest
public class WebLayerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
Refer how to test spring application with only web or using complete application context loading : https://spring.io/guides/gs/testing-web/ refer mocking example : http://www.lucassaldanha.com/unit-and-integration-tests-in-spring-boot/

javax.naming.NoIntialContextException: for JNDI DataSource

I am writing an integrated test for one of my Spring controllers. Test includes a configuration file(data-access-configuration.xml) that has DataSource configured using JNDI. When I am running this test I am getting an error error creating bean with name dataSource nested exception NoIntialContextException. Below is my Test class & Configuration file
Configuration:
<jee:jndi-lookup jndi-name="jobportal_db" id="dataSource"
expected-type="javax.sql.DataSource">
</jee:jndi-lookup>
Test
#RunWith(value=SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { "file:WebContent/WEB-INF/dispatcher-servlet.xml",
"classpath:com/zerosolutions/configuration/service-configuration.xml",
"classpath:com/zerosolutions/configuration/security-configuration.xml",
"classpath:com/zerosolutions/configuration/data-access-configuration.xml",
"classpath:com/zerosolutions/configuration/view-configuration.xml"})
public class TestingFrontController {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getLoginSignupPage() throws Exception{
mockMvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("login"));
}
}
How can I fix it ?
This happens because you don't have a container or JNDI naming service running when you do your unit test.
I write unit tests with separate test config to inject dependencies. I also prefer injecting without Spring so I can use mocks.
My experience is that Spring bean factory slows tests down terribly, especially if I load it for every test class. I'm moving away from using Spring for unit tests beyond JDBC template. Unit tests should use mocks for dependencies.

Spring jdbctemplate, datasource, transactionManager

For a test, i have a abstract class.
public abstract class BaseTestClass
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource)
{
this.setJdbcTemplate(new JdbcTemplate(dataSource));
}
...
}
#Transactional
#ContextConfiguration(locations = {"/spring/test/test-dao-context.xml"})
public class TestUser extends BaseTestClass{
...
}
In test-dao-context.xml file, i have my transaction manager and datasource.
SetDataSource is never called, so i get a null pointerException when i try to do a test.
You need to run you test with an appropriate runner which is SpringJUnit4ClassRunner for Spring driven tests. Otherwise nothing will get injected anywhere as there is no Spring Container managing all the instances.
Check Spring Unit Testing for details.

Categories