Unable to mock JdbcTemplate in testing spring boot service - java

In the test case for service,I am unable to resolve dependency of dao class and JdbcTemplate.
public class TestPromotionUsingJunit {
#InjectMocks
private ItemService itemService;
#Mock
private ItemDAOImpl itemDAOImpl;
#Mock
private JdbcTemplate jdbcTemplate;
#Before
public void setupMock() {
MockitoAnnotations.initMocks(this);
itemService = new ItemService();
}
#Test
public void testFindMax() {
Product pro1 = new Product();
pro1.setPluCode("4900692627408");
pro1.setCategoryNo("2");
pro1.setCategoryName("Women");
pro1.setProductName("T-Shirt10163");
pro1.setColor("CY");
pro1.setSize("32");
BigDecimal b1 = new BigDecimal(94.00);
BigDecimal b2 = new BigDecimal(8);
pro1.setPrice(b1);
pro1.setTax(b2);
Product pro2 = new Product();
pro2.setPluCode("4900692627408");
assertEquals(pro1.getPrice(), itemService.getItem(pro1));
}
}
Here ItemService should return product object but it returns null. Due to internally unable to solve dependency.

#InjectMocks creates a mock instance of itemService, but then
itemService = new ItemService();
creates a real instance and throws the mock away. In setupMock(), after itemService is constructed, try adding the equivalent of this:
itemService.setItemDao(itemDAOImpl);
So you'll have a real ItemService which uses a mock DAO.
Then in testFindMax() configure the DAO to return pro1, something like this:
when(itemDAOImpl.getItem(...)).thenReturn(pro1);

Related

Java - Spring and Mockito - Mocking a constructor parameter that is not a field in class?

I have the following Spring component that I am trying to unit test:
#Component
public class OrderService {
private final DatabaseConnection dbConnection;
private final String collectionName;
#Autowired
public OrderService(
DatabaseConnection dbConnection,
DatabaseConfig databaseConfig) {
this.dbConnection = dbConnection;
this.collectionName = databaseConfig.getCollectionName();
}
//methods etc...
}
The DatabaseConfig class is as follows:
#ConfigurationProperties(prefix = "database")
#ConstructorBinding
public class DatabaseConfig {
//methods etc...
}
I am trying to inject mocks in my OrderService class as follows:
#RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {
#InjectMocks
OrderService orderService;
#Mock
DatabaseConnection dbConnection; // working as expected
#Mock
DatabaseConfig databaseConfig; // giving null pointer
#Mock
DatabaseCollectionConfig databaseCollectionConfig;
#BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);
when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");
}
When I run my test class I get:
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'OrderService' of type 'class com.my.package.OrderService'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
The issue is that in the OrderService constructor when I debug this line is coming as null:
this.collectionName = databaseConfig.getCollectionName();
How can I correctly mock databaseConfig.getCollectionName() to solve the null issue?
No need to use #InjectMock annotation because you are using constructor based injection in your service class. Please rewrite your test case like this and try again.
#RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {
OrderService orderService;
#Mock
DatabaseConnection dbConnection; // working as expected
#Mock
DatabaseConfig databaseConfig; // giving null pointer
#Mock
DatabaseCollectionConfig databaseCollectionConfig;
#BeforeEach
public void setup(){
when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);
when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");
orderService = new OrderService(dbConnection, databaseConfig);
}
}
You can try to create a mock for that method and create an object instance instead of using the InjectMocks annotation.
#RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {
OrderService orderService;
#Mock
DatabaseConnection dbConnection; // working as expected
#Mock
DatabaseConfig databaseConfig; // giving null pointer
#Mock
DatabaseCollectionConfig databaseCollectionConfig;
#BeforeEach
public void setup() {
(...)
when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);
when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");
orderService = new OrderService(dbConnection, databaseConfig);
}
Mock behavior, not values. An #ConfigurationProperties class is just a container for data; it doesn't (generally speaking) do anything. Create a real one with new using your test values (e.g., setCollections(testDatabaseCollectionConfig)).

Why tested class based autowire annotation throw null exception?

I use Spring Boot 5 and JUnit in my project. I create a unit test to test the service.
Here is the service that I am testing:
#Service
#RequiredArgsConstructor
#Slf4j
public class BuilderServiceImpl implements BuilderService{
#Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
#Override
public BuilderDto getByEmail(String email){
}
#Override
public List<BuilderMinDto> getAll() {}
#Override
public List<BuilderMinDto> getAll(int page, int size) {}
#Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
And here is the test class that tests the service above:
#SpringBootTest
#RequiredArgsConstructor
#Slf4j
class BuilderServiceImplTest {
#Mock
private BuilderRepository builderRepository;
#Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
#AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
#Test
void getByEmail(){}
#Test
#Disabled
void getAll() { }
#Test
#Disabled
void testGetAll() {}
#Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test#builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
When I start to run the test class the create method in BuilderServiceImpl fired and on this row:
var str = autoMapper.getDummyText();
I get NullPointerException(autoMapper instance is null).
Here is the definition of AutoMapper class:
#Component
#Slf4j
#RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
As you can see I use #Component annotation to register the AutoMapper class to the IoC container and Autowired annotation to inject it into autoMapper property in BuilderServiceImpl class.
Why autoMapper instance is null? How can I make autoMapper to be initialized?
In order to make #Autowire work you have to use the instance of BuilderServiceImpl (object under test) created by spring itself.
When you create the object like this (by yourself, manually):
#BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring doesn't know anything about this object, hence Autowiring won't work
Another thing that might be useful:
You've used #Mock for BuilderRepository and AdminUserRepository.
This are plain mockito annotation, and if you're using an integration/system test that runs the spring under the hood, probably this is not what you want:
Surely, it will create a mock, but it won't put it onto an application context, and won't substitute the beans of these classes that might have been created by spring.
So if this is what you want to achieve, you should use #MockBean instead.
This annotation belongs to Spring Testing framework rather than a plain mockito annotation.
All-in-all you might end up with something like this:
#SpringBootTest
class MyTest
{
#MockBean
BuilderRepository builderRepo;
#MockBean
AdminUserRepository adminUserRepo;
#Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
#Test
void mytest() {
...
}
}
Add #Autowire annotation Annotations on below fields. Error due your not initialized below object In BuilderServiceImpl
#Autowire
private final BuilderRepository builderRepository;
#Autowire
private final AdminUserRepository adminUserRepository;
Why are you creating BuildService manually? If you do this, set AutoMapper manualy too.
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
You are not using di.

Data can't fetch from db - mockito

I am using the Mockito framework for my jUnit testing.
I am not able to fetch data from the database. Fetches null or empty from the db. Not sure how can I mock the entityManager.
public class AppServiceTest {
#Mock
private EntityManager entityManager;
#Mock
DataSource mockDataSource;
#Mock
Connection mockConn;
#Mock
PreparedStatement mockPreparedStmnt;
#Mock
ResultSet mockResultSet;
#Mock
private Query query;
#Test
public void testGetAllDc() throws NamingException {
AppDataService appDataService = Mockito.mock(AppDataService.class);
List<String> customResults = new ArrayList<String>();
//customResults = ....
System.out.println("**RESULTS**"+appDataService.getAllDc()); // here it is printed as []
Mockito.when(appDataService.getAllDc()).thenReturn(customResults);
}
}
AppDataService.java:
#Stateless
public class AppDataService{
#PersistenceContext
EntityManager entityManager;
public List<Object> getAllDc() {
try {
Query query = entityManager.createQuery("myQuery");
List<String> allDc= query.getResultList();
}
//......
}
Update:
Its observed that the mock values can be detected for entityManager as Mock for EntityManager, hashCode: 5425710. But entityManager.getCriteriaBuilder() or whatever entityManager.get....() are getting null in the AppDataService class .
In your test, you create a mock AppDataService appDataService = Mockito.mock(AppDataService.class);.
In order to test this class, you must not mock it. Either create it using new AppDataService() or let mockito do that for you (#InjectMocks)
To initialize the mocks or the class-under-test you may do this by using annotations and either use the mockito runner:
#RunWith(MockitoJunitRunner.class)
public class AppServiceTest {
#InjectMock
private AppService appService;
#Mock
private EntityManager entityManager;
or
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
Further, you mocked entitymanager, therefore all of it's methods are stubbed and return no values (null) per default on any method call.
If you expect the createQuery("myQuery") invocation to return something, you should stub it properly after mock creation/injection in the setup method.
#Before
public void setup() {
//...
when(entityManager.createQuery(eq("myQuery"))).thenReturn(query);
//OR
when(entityManager.createQuery(anyString())).thenReturn(query)
If it doesn't matter what the mocks return as long as it is not null, you may initialize the mocks using
#Mock(answer = Answers.RETURNS_DEEP_STUBS)) //OR #Mock(answer = Answers.RETURNS_MOCKS)
private EntityManager entityManager;
So the whole test should look like
#RunWith(MockitoJunitRunner.class)
public class AppServiceTest {
#Mock(answer = Answers.RETURNS_DEEP_STUBS))
private EntityManager entityManager;
#Mock
private Query query;
...
#InjectMocks
private AppDataService appDataService;
#Test
public void testGetAllDc() throws NamingException {
//arrange
List<String> customResults = new ArrayList<String>();
//TODO populate customResults
when(entityManager.createQuery("myQuery")).thenReturn(query);
when(query.getResultList()).thenReturn(customResults);
//act
System.out.println("**RESULTS**"+appDataService.getAllDc());
You don't need to mock appDataService if you want to test it. This field should be annotated with #InjectMocks.
#InjectMocks
private AppDataService appDataService = new AppDataService();
What you need to mock is entityManager.createQuery and query.getResultList methods.
Query queryMock = mock(Query.class);
when(entityManager.createQuery(anyString())).thenReturn(queryMock);
when(query.getResultList()).thenReturn(customResults);
After taht you can call appDataService.getAllDc() in your test and check the results.

Mockito mocking objects from inside local method

In my class, dependencies are injected by Spring. During testing, I am using mocks. I am getting null pointer exception when I call sys.getId("abc12345") in the following code. I am wondering how to write a unit test that gets 100% coverage.
Class under test:
public class SystemUT implements SUTIface{
#Inject
private AccountLookupDAO dao;
#Inject
private OrchService service;
public Response perform(Request req){
String sellerId = getId(request.getSeller().getNum());
String buyerId = null;
if(req.getBuyerId){
buyerId = getId(request.getBuyer().getNum())
}
service.execute(Request,sellerId,buyerId)
}
String getId(String num){
PrefAcct prefAcctObj = dao.lookupPrefId(num,Consants.StrArr);
PrefSysOfRecObj sorObj= prefAcctObj.getSysOfRecord();
return sorObj.getId();
}
}
Unit test:
public Class SystemUTTest{
#Mock
SystemUT sys;
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
#Mock
PrefAcct prefAcctObj;
#Mock
PrefSysOfRecObj sorObj;
#Before
public void setup(){
Whitebox.setInternalState(sys, daoMock, serviceMock);
}
#Test
public test getId(){
when(dao.lookupPrefId(any(String.class), any(String[].class))).thenReturn(prefAcctObj);
when(prefAcctObj.getSysOfRecord()).thenReturn(sorObj);
when(sorObj.getId()).thenReturn("185");
assertEquals("185",sys.getId("abc12345"));
}
}
Your problem is that your SystemUT class doesn't have its dependencies injected. You could have Spring do this by using their JUnitRunner, but it's not really a unit test then, since you'd be letting Spring dictate which dependencies get injected. Really, you want to control them, and one way to do that is to transform your class to expose its dependencies via a constructor:
public class SystemUT implements SUTIface{
private final AccountLookupDAO dao;
private final OrchService service;
#Inject
public SystemUT(AccountLookupDAO dao, OrchService service) {
this.dao = dao;
this.service = service;
}
}
This will function identically to your current approach since Spring is able to inject dependencies using a constructor annotated with #Inject. Now, when you instantiate your SystemUT class for test, pass mocked objects for its dependencies:
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
private SystemUT sys;
#Before
public void setup(){
sys = new SystemUT(daoMock, serviceMock);
Whitebox.setInternalState(sys, daoMock, serviceMock);
}

How to mock a autowired list of Spring beans?

I've read plenty of articles about how to mock Spring's bean and their autowired fields. But there is nothing I could find about autowired lists of beans.
Concrete problem
I've a class called FormValidatorManager. This class loop through several validators which implements IFormValidator.
#Component
public class FormValidatorManager implements IValidatorManager {
#Autowired
private List<IFormValidator> validators;
#Override
public final IFieldError validate(ColumnDTO columnToValidate, String sentValue) {
String loweredColName = columnToValidate.getName().toLowerCase();
IFieldError errorField = new FieldError(loweredColName);
for (IEsmFormValidator validator : validators) {
List<String> errrorsFound = validator.validate(columnToValidate, sentValue);
//les erreurs ne doivent pas ĂȘtre cumulĂ©es.
if(CollectionUtils.isNotEmpty(errrorsFound)){
errorField.addErrors(errrorsFound);
break;
}
}
return errorField;
}
}
I would like to test this class. But I can't find a way to mock validators property.
What I've tried
Since IFormValidators are singleton, I tried to mock several instances of these beans hoping them to be reflected in FormValidatorManager.validators but without success.
Then, I tried to create a list of IFormValidators which was annotated as #Mock. By initiating the List manually, I was hoping initMocks() to inject the created list. That was still without success.
Here is my last try:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:spring/test-validator-context.xml"})
public class FormValidatorManagerTest {
#Mock
private RegexValidator regexValidator;
#Mock
private FormNotNullValidator notNullValidator;
#Mock
private FormDataTypeValidator dataValidator;
#InjectMocks
private FormValidatorManager validatorManager;
#Mock
private List<IEsmFormValidator> validators = new ArrayList<IEsmFormValidator>();
#Mock
private ColumnDTO columnDTO;
#Before
public void init() {
validators.add(notNullValidator);
validators.add(regexValidator);
validators.add(dataValidator);
MockitoAnnotations.initMocks(this);
Mockito.when(columnDTO.getTitle()).thenReturn("Mock title");
Mockito.when(columnDTO.getName()).thenReturn("Mock name");
}
#Test
public void testNoErrorFound(){
mockValidator(notNullValidator, new ArrayList<String>());
mockValidator(regexValidator, new ArrayList<String>());
mockValidator(dataValidator, new ArrayList<String>());
IFieldError fieldErrors = validatorManager.validate(columnDTO, "Not null value");
Assert.assertEquals(0, fieldErrors.getErrors().size());
verifyNumberOfValidateCalls(regexValidator, Mockito.atMost(1));
verifyNumberOfValidateCalls(dataValidator, Mockito.atMost(1));
verifyNumberOfValidateCalls(notNullValidator, Mockito.atMost(1));
}
private void mockValidator(IFormValidator validator, List<String> listToReturn){
Mockito.when(validator.validate(Mockito.any(ColumnDTO.class), Mockito.anyString())).thenReturn( listToReturn );
}
private void verifyNumberOfValidateCalls(IFormValidator validator, VerificationMode verifMode){
Mockito.verify(validator, verifMode).validate(Mockito.any(ColumnDTO.class), Mockito.anyString());
}
}
An NPE is thrown in IFormValidator.validate() which I thougth would be mocked. The concrete implementation should not be called.
This leads to a really bad behavior since some of my tests on that class are false positives while others completly fail.
I'm trying to figure out how to mock an autowired list of beans while still having the possibility to mock specific implementations.
Do you have an idea start of solution ?
Regards
I finally figured it out...
Sometimes, asking a question can give you a better approach to your problems :p
The problem is I was linking the validators to the list before they were mocked. The validators was then null and no reference could be updated when the MockitAnnotations.initMocks(this) was called.
Moreover, to avoid iterator problems on List, I had to use #Spy instead of #Mock.
Here is the final solution:
#Mock
private EsmRegexValidator regexValidator;
#Mock
private EsmFormNotNullValidator notNullValidator;
#Mock
private EsmFormDataTypeValidator dataValidator;
#InjectMocks
private EsmFormValidatorManager validatorManager;
#Spy
private List<IEsmFormValidator> validators = new ArrayList<IEsmFormValidator>();
#Mock
private ColumnDTO columnDTO;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
validators.add(notNullValidator);
validators.add(regexValidator);
validators.add(dataValidator);
Mockito.when(columnDTO.getTitle()).thenReturn("Mock title");
Mockito.when(columnDTO.getName()).thenReturn("Mock name");
}
Adding another answer when dealing with multiple list of beans. Mockito doesnt know anything about the generics it just uses random list provided so in my case something like this happened.
Which threw the ClassCastException because the bean injection was not perfomed correctly. Expecting SfmcImportRepository but injection was SfmcParser
#Mock SfmcEmailsCsvFileParser emailParser;
#Mock SfmcSmsCsvFileParser smsParser;
#Mock SfmcSmsRepository smsRepository;
#Mock SfmcEmailRepository emailRepository;
List<SfmcImportRepository> sfmcImportRepositories = new ArrayList<>();
List<SfmcParser> sfmcParsers = new ArrayList<>();
SfmcFtpService service;
#Before
public void init() {
sfmcImportRepositories.add(emailRepository);
sfmcImportRepositories.add(smsRepository);
sfmcParsers.add(smsParser);
sfmcParsers.add(emailParser);
service = new SfmcFtpService(sfmcImportRepositories, sfmcParsers);
}
method initMocks is deprecated in the recent versions and is no longer needed:
#Mock
private SomeTxHandler1 txHandler1;
#Mock
private SomeTxHandler2 txHandler2;
#Spy
private final List<TxHandler> txHandlers = new ArrayList<>();
#Spy // if you want to mock your service's methods
#InjectMocks
private MyService myService;
#BeforeEach
public void init() {
lenient().when(txHandler1.accept(...)).thenReturn(true);
txHandlers.add(txHandler1);
lenient().when(txHandler2.accept(...)).thenReturn(true);
txHandlers.add(txHandler2);
}

Categories