Not able to mock jdbcTemplate(datasource) in springboot test class using testNG - spring-boot

I am getting a null pointer exception on jdbcTemplate object in this MyMain.java class while mocking the jdbcTemplate.update() method. I have used #Mock for this in the MyMainTest.java class. Here are my code snippets:
MyMain.java
#Component
public class MyMain {
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("myDatasource")
Datasource myDatasource;
#PostConstruct
public void initialize() {
jdbcTemplate = new JdbcTemplate(myDatasource);
}
public void saveData() {
jdbcTemplate.update("query", "parameter passing here"); // In this line only I am getting Nullpointer exception on jdbcTemplate.
}
}
MyMainTest.java
public class MyMainTest {
#Mock
JdbcTemplate jdbcTemplate;
#Test
public void saveDataTest() {
when(jdbcTemplate.update(Mockito.any(), Mockito.any()).thenReturn(1);
new MyMain().saveData();
}
}
I have tried several alternative ways and I am still getting this issue.
Note: I am using TestNG for running the test classes. It's a Spring Boot project.

Related

How to mock a ObjectProvider<XXX> that is autowired?

I am doing a migration from Spring 4.x to 5.x and am following the recommendation to wrap the object with an ObjectProvider to handle beans that return null: https://stackoverflow.com/a/49393682/10863988
This is the class set up I have:
class ConfigurationClass{
#Autowired
private ObjectProvider<MyObject> myObject;
public SomeOtherClass getSomeOtherClass() {
return new SomeOtherClass(myObject.getIfAvailable());
}
}
class TestSomeOtherClass {
#Mock
MyObject myObject;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
The problem is when I run this test. the myObject in the ConfigurationClass returns a null pointer exception.
I've tried adding this to the TestSomeOtherClass but I still can't seem to mock the ObjectProvider<MyObject>:
class TestSomeOtherClass {
#Mock
MyObject myObject;
#Mock
ObjectProvider<MyObject> myObjectObjectProvider;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
doReturn(myObject).when(myObjectObjectProvider).getIfAvailable();
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
Any advice on how to handle this?
You do not tell Mockito to handle it's annotations (#Mock, #InjectMocks) anywhere in your code, so they do not have any effect. By default all non-primitive fields in Java are initialized as null - that's where the NullPointerException comes from.
openMocks/initMocks method
Depending on the version of Mockito you're using, you need to call initMocks() or openMocks() static method from the MockitoAnnotations class:
AutoCloseable openMocks;
#BeforeEach
public void setup() {
// the line below is where the magic happens
openMocks = MockitoAnnotations.openMocks(this);
doReturn(myObject).when(myObjectObjectProvider)
.getIfAvailable();
someOtherClass = spy(configuration.getSomeOtherClass());
}
#AfterEach
void tearDown() throws Exception {
openMocks.close();
}
#Test
void test() {
assertNotNull(someOtherClass);
}
#ExtendWith(MockitoExtension.class)
You can also use the #ExtendWith(MockitoExtension.class) annotation over your class and it has the same effect as the methods described above.
You can find both approaches tested in a GitHub repository I've created (all tests pass).

How to unit test a method which uses spring retry mechanism

I am trying to write a Junit method that uses the spring retry mechanism to re-invoke a failed operation.
But I am not able to verify spring retry working properly with JUnit.
public interface StudentService{
public void addStudent(Student student);
}
#Service
public class StudentServiceImpl {
#Autowired
SomeService someService;
#Transactional
// InternalServerErrorException runtime exception
#Retryable(value = {InternalServerErrorException.class},
maxAttempts=6)
public void addStudent(Student student){
try{
someService.addStudent(student);
}catch(Exception e){
throw new InternalServerErrorException("unable to add student");
}
}
}
#Configuration
##EnableRetry
public class AppConfig{
}
//
#RunWith(SpringJUnit4ClassRunner.class)
public class StudentServiceImplTest(){
#InjectMocks
StudentServiceImpl classUnderTest;
#Mock
SomeService someService;
public void testAddStudent(){
//ARRANGE
Student student= new Student("John","A123") // name, Id
doThrow(InternalServerErrorException).doNothing().when(someService).addStudent(student);
//ACT
classUnderTest.addStudent(student);
//ASSERT 1st attempt got exception , 2nd attempt success
// Always failed with exception
verify(someService, times(2)).addStudent(any());
}
}
// getting following exception
com.studentapp.exceptions.InternalServerErrorException: unable to add student
#InjectMocks
StudentServiceImpl classUnderTest;
You are injecting it as a Mock instead of using Spring #Autowired to get the full Spring proxy with the retry interceptor.

passing program arguments to spring boot

I have a spring boot batch application which use program arguments to get some files and manipulate it. the app works fine but i have problems when running the junit tests.
Here is my code :
#Component
public class ApplicationArguments implements InitializingBean {
#Autowired private org.springframework.boot.ApplicationArguments appArgs;
private String filePath;
#Override
public void afterPropertiesSet() throws Exception {
filePath = appArgs.getSourceArgs()[0];
}
}
this bean is used by another one to build the full path :
#Component
public class InitPaths implements InitializingBean {
#Autowired private ApplicationArguments myAppArgs;
private String fullPath;
#Override
public void afterPropertiesSet() throws Exception {
fullPath = myAppArgs.getFilePath(); //this will be null when launching tests
fullPath.toString();//this will throw a NullPointerException if we run the test
}
}
the application works fine using this command :
java -jar myApp.jar fileName.txt
is there any solution to pass the same argument to the junit test ?
I tried to use mocks but i had the same issue :
#RunWith(SpringRunner.class)
#SpringBootTest
public class BatchTest {
#MockBean
ApplicationArguments applicationArguments;
#Autowired
#InjectMocks
InitPaths initPaths;
#Before
public void before() {
when(applicationArguments.getFilePath()).thenReturn("myCustomFile.dat");
}
#Test
public void contextLoad() {
}
}
Here is the error :
Invocation of init method failed; nested exception is java.lang.NullPointerException
The problem is because method afterPropertiesSet() in InitPaths have been running a earlier them before in test. Thats mean your mocked ApplicationArguments do not have any mocked behavior. From my perspective you might create new mocked ApplicationArguments with predefined behavior
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(ApplicationArgumentsTestConfig.class)
public class BatchTest {
#Autowired
InitPaths initPaths;
#Test
public void contextLoad() {
}
public static class ApplicationArgumentsTestConfig {
#Bean
#Primary
public ApplicationArguments mockArg() {
ApplicationArguments mocked = Mockito.mock(ApplicationArguments.class);
Mockito.when(mocked.getFilePath()).thenReturn("test-mock-path");
return mocked;
}
}
}
I just checked work for me.

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;

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.

Resources