Spring Boot Datasource in unit tests - java

I have a simple Spring Boot web app, that reads from a database and return a JSON response. I have the following test configuration:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=MyApplication.class, properties={"spring.config.name=myapp"})
#AutoConfigureMockMvc
public class ControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private ProductRepository productRepo;
#MockBean
private MonitorRepository monitorRepo;
#Before
public void setupMock() {
Mockito.when(productRepo.findProducts(anyString(), anyString()))
.thenReturn(Arrays.asList(dummyProduct()));
}
#Test
public void expectBadRequestWhenNoParamters() throws Exception {
mvc.perform(get("/products"))
.andExpect(status().is(400))
.andExpect(jsonPath("$.advice.status", is("ERROR")));
}
//other tests
}
I have a DataSource bean that is configured in the main configuration of the application. When I run the tests Spring tries to load the context and fails, because the datasource is taken from JNDI. In general I want to avoid creating a datasource for this tests, because I have the repositories mocked.
Is it possible to skip the creation of datasource when running the unit tests?
In memory database for testing is not an option, because my database creation script has a specific structure and cannot be easily executed from classpath:schema.sql
Edit
The datasource is defined in MyApplication.class
#Bean
DataSource dataSource(DatabaseProeprties databaseProps) throws NamingException {
DataSource dataSource = null;
JndiTemplate jndi = new JndiTemplate();
setJndiEnvironment(databaseProps, jndi);
try {
dataSource = jndi.lookup(databaseProps.getName(), DataSource.class);
} catch (NamingException e) {
logger.error("Exception loading JNDI datasource", e);
throw e;
}
return dataSource;
}

Since you are loading configuration class MyApplication.class datasource bean will be created, Try moving datasource in another bean which is not used in a test, make sure all classes loaded for tests are not dependant on datasource. Or In your tests create a config class marked with #TestConfiguration and include it in SpringBootTest(classes=TestConfig.class) mocking data source there like
#Bean
public DataSource dataSource() {
return Mockito.mock(DataSource.class);
}
But this may fail since method call to this mocked datasouce for connection will return null, In that case, you'll have to create an in-memory datasource and then mock jdbcTemplate and rest of dependencies.

Try adding your datasource as a #MockBean too:
#MockBean
private DataSource dataSource
That way Spring will do the replacing logic for you, having the advantage that your production code bean creation won't even be executed (no JNDI lookup).

Related

Mark externally-declared bean as #Primary

I have a Spring Boot app that requires multiple datasources that will be used via JdbcTemplates to make sql statements to different DBs. These datasource beans are already declared in external dependencies that I am pulling into my app via an #Import statement the JdbcTemplate-bean-declaring java-based #Configuration classes.
After declaring two different JdbcTemplates initialized with two different DataSource beans form these external dependencies, I run into the following error on app startup:
Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found:
Given that I don't have access to the declarations of these DataSource beans in my app, how can I mark one as #Primary?
EDIT: Here's code for the construction of the JdbcTemplates (Note "dataSourceA" and "dataSourceB" are pulled from an external dependency)
#Configuration
#Import({DataSourceAApplicationConfig.class, DataSourceBApplicationConfig.class})
public class JdbcTemplateAppConfig {
#Autowired
DataSource dataSourceA;
#Autowired
DataSource dataSourceB;
#Bean
#Primary
public JdbcTemplate jdbcTemplateForDataSourceA(#Qualifier("dataSourceA") DataSource dataSource) {
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
#Bean
public JdbcTemplate jdbcTemplateForDataSourceB#Qualifier("dataSourceB") DataSource dataSource){
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
}

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/

Spring Boot: Use Hibernate Session in a unit test

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.

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