how to inject multiple JdbcOperations into Spring Test Case - spring

Here is my Code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/load-BMS-data-job-launcher-context.xml" })
public class SimpleJobLaunchFunctionalTests {
#Autowired
private JobLauncherTestUtils jobLauncherUtils;
#Autowired
private JdbcOperations jdbcTemplate;
#Autowired
private JdbcOperations jdbcTemplateBMS;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Autowired
public void setDataSource(DataSource BMSdataSource) {
this.jdbcTemplateBMS = new JdbcTemplate(BMSdataSource);
}
#Before
public void setUp() {
jdbcTemplate.update("DELETE from SHADOW_BMS");
// jdbcTemplate.update("DELETE from CMNREF.CNTRCT_EXTRNL_KEY_REF_V");
jdbcTemplateBMS.update("DELETE from CNTRCT_EXTRNL_KEY_REF_V");
I want to wire two separate JdbcTemplates to operate on two distinct databases inside this test class. I fail to know how to set different dataSources - I get an exception when I try to invoke the second setDataSource(DataSource BMSdataSource) method.
How do I do that?

You can create beans with name="template1" and name="template2" (with annotations #Bean(name = "template1") ). In the test you use them as follows:
#Qualifier("template1")
#Autowired
private JdbcOperations jdbcTemplate1;
#Qualifier("template2")
#Autowired
private JdbcOperations jdbcTemplate2;

Related

Not able to mock jdbcTemplate in Spring boot Test class

I am using Spring boot and Mockito for testing. I have been able to write test cases for Service layer and they re working fine. But, the test cases for DAO layer do not. The jdbcTemplate object that is mocked and autowired gives null pointer when executing the test case. Below are the details:
My DAOTest class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = EcommerceApplication.class)
public classEcommerceDaoTest {
#InjectMocks
private IEcommerceDao ecommerceDao = new EcommerceDaoImpl();
#Mock
#Autowired
private JdbcTemplate as400JdbcTemplate;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
}
#Test
public void checkOrderExistsTest() throws EcommerceException{
Mockito.when(as400JdbcTemplate.queryForObject(queryForOrder,new Object[]
{"1000"}, int.class)).thenReturn(1);
boolean exists =
ecommerceDao.checkOrderExists("1000");
assertTrue(exists);
}
}
EcommerceDAOImpl.java:
#Override
public boolean checkOrderExists(String orderNo)throws EcommerceException{
boolean doesExist = false;
int count = 0;
try{
count= as400JdbcTemplate.queryForObject(queryForOrder, new Object[]{orderNo}, int.class);
if(count >0){
doesExist = true;
}
}
catch(Exception e){
}
return doesExist;
}
AS400Config.java:
#Bean
#Autowired
public JdbcTemplate as400JdbcTemplate(#Qualifier("as400DataSource")DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
ECommerceApplication.java
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#EnableTransactionManagement
#Import(As400Configuration.class)
public class EcommerceApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceApplication.class, args);
}
}
When I am running the test case, I am getting NullPointerException for as400JdbcTemplate. The functionality works fine as is. Its just the test cases for DAO layer that fail because of the inability of the jdbcTemplate to get mocked/autowired.
Please let me know where I am going wrong.
You don't need to use #Mock and #Autowired at the same time. Use only #Mock:
#Mock
private JdbcTemplate as400JdbcTemplate;
Use instead of #RunWith(SpringRunner.class) --> #RunWith(MockitoJUnitRunner.class)
Also to inject mock into DAO you can use ReflectionTestUtils from spring test.
public static void setField(Class targetClass, String name, Object value)
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(ecommerceDao ,"as400JdbcTemplate" ,
as400JdbcTemplate);
}
#Mock
private JdbcTemplate as400JdbcTemplate;

Using #SpyBean with #Qualifier Spring Boot Test

I have 2 DataSources in my app.
So, to get the required JdbcTemplate, i use #Qualifier. But, when i do like below, the test runs... but stays waiting indefinitely, if there is any use of JdbcTemplate in the "Method Under Test".
#Service
#Transactional
public class SampleDatabaseService {
#Autowired
#Qualifier("firstDbJdbcTemplate")
private JdbcTemplate firstDbJdbcTemplate;
#Autowired
#Qualifier("secondDbJdbcTemplate")
private JdbcTemplate secondDbJdbcTemplate;
#Cacheable("status")
public Map<String, Device> readAllValidDeviceStatus() {
Map<String, Device> allDeviceStatuses = new HashMap<>();
//Stops at below line indefinitely if "SpyBean" is used
List<StatusDetail> statusDetails = firstDbJdbcTemplate
.query(SqlQueries.READ_DEVICE_STATUS, BeanPropertyRowMapper.newInstance(StatusDetail.class));
statusDetails
.stream()
.filter(deviceStatus -> deviceStatus.getName() != "Some Invalid Name")
.forEach(deviceStatus -> allDeviceStatuses
.put(deviceStatus.getName(), buildDevice(deviceStatus)));
return allDeviceStatuses;
}
/** More Stuff **/
}
and the Test :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Transactional
#Rollback
#ActiveProfiles("test")
public class SampleDatabaseServiceTest {
#SpyBean
#Qualifier("firstDbJdbcTemplate")
private JdbcTemplate firstDbJdbcTemplate;
#Autowired
private SampleDatabaseService serviceUnderTest;
#Before
public void populateTables() {
//Insert some Dummy Records in "InMemory HSQL DB" using firstDbJdbcTemplate
}
#Test
public void testReadAllValidDeviceStatus() {
// When
Map<String, Device> allDeviceStatuses = serviceUnderTest.readAllValidDeviceStatus();
// Then
assertThat(allDeviceStatuses).isNotNull().isNotEmpty();
// More checks
}
/* More Tests */
}
But, when i replace the #SpyBean with #Autowired in Test, it works fine.
Why is it so? Any help is greatly appreciated. :)
Use it in below format
#MockBean(name = "firstDbJdbcTemplate")
private JdbcTemplate firstDbJdbcTemplate;

How to use Multiple JdbcOperations and Multiple JdbcTemplates in Spring

I have 2 different datasrouces from which I want to use in the same file and query each of them using JdbcOperations implementation. Is this possible?
#Repository
public class TestRepository {
private JdbcOperations jdbcOperations;
#Inject
#Qualifier("dataSource1")
private DataSource dataSource1;
#Inject
#Qualifier("dataSource2")
private DataSource dataSource2;
#Bean
#Qualifier("jdbcTemplate1")
public JdbcTemplate jdbcTemplate1(#Qualifier("dataSource1") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
#Qualifier("jdbcTemplate2")
public JdbcTemplate jdbcTemplate1(#Qualifier("dataSource2") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Inject
public TestRepository(JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations; //HOW DO I SPECIFY WHICH JDBCTEMPLATE SHOULD BE USED FOR INITIALIZING THIS JDBCOPERATIONS
}
}
Above is my code, note that JdbcOperations is initialized in the constructor. But no way to specify which jdbcTemplate should the jdbcOperations use.
The qualifier should actually be put at the parameter level:
public TestRepository(#Qualifier("jdbcTemplate2")JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations;
}
Uses the bean named jdbcTemplate2

Singleton vs prototype JdbcTemplate

In the Spring documentation the recommended way to use JdbcTemplate is to create new template for every class you use it in...
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
I was wondering, what is the advantage of this solution over define the jdbctemplate as singleton in the context and directly inject it in the Dao
public class JdbcCorporateEventDao implements CorporateEventDao {
#Autowired
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
From the class-level documentation of JdbcTemplate:
* Can be used within a service implementation via direct instantiation
* with a DataSource reference, or get prepared in an application context
* and given to services as bean reference.
Either is ok. Here I have a large application (50 DAOs, 100 concurrent users) and there is one jdbcTemplate object for the entire application, defined in the spring context. This works fine.
One drawback of injecting the JdbcTemplate directly is if you need to/decide to use a SQLExceptionTranslator.
If your JdbcTemplate is a singleton, setting a SQLExceptionTranslator in any class affects all classes using that template.
For example...
public class JbdcUserDAO implements UserDAO{
#Autowired
private JdbcTemplate jdbcTemplate;
public JbdcUserDAO() {
this.jdbcTemplate.setExceptionTranslator(new UserSQLExceptionTranslator());
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
AND
public class JdbcCompanyDAO implements CompanyDAO{
#Autowired
private JdbcTemplate jdbcTemplate;
public JdbcCompanyDAO() {
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
SQLExceptions raised through JdbcCompanyDAO will also be run through UserSQLExceptionTranslator even though it looks like no translator is registered.

Spring JUnit testing with #Autowired annotation

I´m having issues with my test cases after having introduced #Autowired in one of the classes under test.
My testcase now looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext.xml", "/spring-security.xml"})
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
private StudentRepository studentRepository;
private CompanyRepository companyRepository;
private Student testStudent;
private Company testCompany;
#Before
public void setUp() {
studentRepository = new StudentRepository();
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
#Test
....
}
StudentRepository now looks like this:
#Service
public class StudentRepository extends AbstractRepository<Student> {
...
private PasswordEncoder passwordEncoder;
private MailService mailService;
public StudentRepository() {
// TODO Auto-generated constructor stub
}
#Autowired
public StudentRepository(MailService mailService, PasswordEncoder passwordEncoder) {
this.mailService = mailService;
this.passwordEncoder = passwordEncoder;
}
Obviously this test case won´t work anymore.
But what changes do I need to make to the testcase for the #Autowired annotation to be picked up by the test case?
EDIT:
I´ve now updated my setUp() to this (I need the password encoder to avoid null password):
#Before
public void setUp() {
//studentRepository = new StudentRepository();
studentRepository = new StudentRepository(mock(MailService.class), ctx.getAutowireCapableBeanFactory().createBean(ShaPasswordEncoder.class));
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
My testcase is now running OK, but my testsuite failes with a NullPointerException.
I´m guessing the ApplicationContext is not being Autowired when running the testsuite for some reason?
If you don't want to declare your StudentRepository in one of XML files referenced by #ContextConfiguration and autowire it into the test, you can try to use AutowireCapableBeanFactory as follows:
...
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
...
#Autowired ApplicationContext ctx;
#Before
public void setUp() {
studentRepository = ctx.getAutowireCapableBeanFactory()
.createBean(StudentRepository.class);
...
}
...
}

Resources