How to mock joins in queryDsl (jpa + mockito)? - java

Given the queryDsl code that selects from entities by joining them
#Repository
#RequiredArgsConstructor
public class BlahRepository {
private final JPAQueryFactory jpaQueryFactory;
public List<YyzPerEntity> getYyzPerById(Long id) {
final QYyzPerEntity n = YyzPerEntity;
final QDufEntity d = dufEntity;
final QWofEntity g = wofEntity;
return jpaQueryFactory
.selectFrom(n)
.leftJoin(d)
.on(n.pLnr.eq(d.dufEntityPK.dofx))
.leftJoin(g)
.on(d.wof.getID().eq(g.Id))
.where(g.Id.eq(Id))
.fetch();
}
I want to mock the join part like:
#Mock private JPAQueryFactory queryFactory;
private JPAQuery queryMock;
#InjectMocks private BlahRepository blahRepository;
#Test
void byId() {
// arrange
queryMock = mock(JPAQuery.class);
when(queryFactory.selectFrom(QYyzPerEntity.yzPerEntity))
.thenReturn(queryMock);
// how to mock joins here... ?
when(queryMock.fetch()).thenReturn(List.of(QYyzPerEntity.yzPerEntity));
// act
List<YyzPerEntity> yyzPerById =
blahRepository.getYyzPerById(123L);
// assert
assertThat(yyzPerById).isNotNull();
any ideas what I could try?
Error that I get is:
java.lang.NullPointerException: Cannot invoke com.querydsl.jpa.impl.JPAQuery.on(...) because the return value of com.querydsl.jpa.impl.JPAQuery.leftJoin(com.querydsl.core.types.EntityPath) is null
Is it possible at all or cannot be mocked ?

The easiest way to mock builder API's, like for example JPAQuery, is to use Answers.RETURNS_SELF as default stub.
For example, use the following to instantiate your queryMock:
#Mock(answer = Answers.RETURNS_SELF) JPAQuery queryMock;

Related

Mock returned value of ResultSet inside the method to be tested that implements an interface

I'm trying to mock some data with Mockito for some tests, but I can't get it to work. My problem is that the value I want to mock is the return of a call inside the method I need to test.
I have a class, MyAccessor.java (that implements a service, MyService.java) that inside has a method, getMyMethod(String value), and inside that method there is a call to the DB:
final List<MyType> results = this.jdbcTemplate.query(sqlQuery, new RowMapper<MyType>() {
#Override
public MyType mapRow(final ResultSet rs, final int rowNum) throws SQLException {
final int fieldA = rs.getInt("column_name_1");
final int fieldB = rs.getInt("column_name_2");
final int fieldC = rs.getInt("column_name_3");
final int fieldD = rs.getInt("column_name_4");
return new MyType(fieldA , fieldB , fieldC , fieldD);
}
}, value);
There is a case where due to timeouts with the database, an empty list is returned by the rowMapper. So in that case it is not possible to do return results.get(0) because you will get an ArrayIndexOutOfBoundsException.
So I have a check:
if (!results.isEmpty()) {
return results.get(0);
} else {
return new MyType(0, 0, 0, 0);
}
Now, I want to test this particular scenario but I don't know how to mock the resultSet return so that the rowMapper returns an empty list. In my test class I have set:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { ".....", "....."})
#TestPropertySource(".....")
public class MyClassTest {
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyService myService;
#Before
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
#Test
public void myTestMethod() {
Mockito.when(this.jdbcTemplate.query("SELECT NULL LIMIT 0", new RowMapper<Object>() {
#Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new ArrayList<>();
}
})).thenReturn(new ArrayList<Object>());
MyType result = this.myService.getMyMethod("value");
assertEquals(0, result.getFieldA());
assertEquals(0, result.getFieldB());
assertEquals(0, result.getFieldC());
assertEquals(0, result.getFieldD());
}
}
But the test fails because the result returned is from the database instead of an empty list to test the timeout case.
I think the problem may be because I'm injecting the mock in the service interface instead of the implementation, but I wanted to know if it can be done somehow so I don't have to test the mock for each implementation of that interface.
Or maybe I am using the Mockito framework wrong and in that case, how can I test correctly?
Regads.
I think you want something more like:
#Test
public void myTestMethod() {
Mockito.when(this.jdbcTemplate.query(eq("SELECT NULL LIMIT 0"), Mockito.any(RowMapper.class))
.thenReturn(new ArrayList<Object>());
}
Reason is because when mocking, you were saying only to return the empty list if both arguments to the query is exactly as you defined. In your case, you don't care about the exact instance of RowMapper.
I have found the problem. I was injecting the mock in the interface and not in the implementation of the interface. So I had to change:
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyService myService;
to:
#Mock
private JdbcTemplate jdbcTemplate;
#InjectMocks
#Autowired
private MyAccessor myAccessor;
#Autowired
private MyService myService;
And inside the test this:
MyType result = this.myService.getMyMethod("value");
to:
MyType result = this.myAccessor.getMyMethod("value");
I don't know if there is a better way to do this, without having to instantiate all the implementations that the service may have.
Regards.

Mocking a function without argument containing CriteriaBuilder is it possible?

I think the reason why is impossible to mock criteriabuilder is because it only response to the database created in postgres. If that's not the case then how can solve this error (shown below).
class MessageRest
public List<Message> getListAll(){
logger.info("Get all messages");
return messageRepository.getAll();
}
class MessageRepository
public List<Message> getAll(){
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Message> criteria = criteriaBuilder.createQuery(Message.class);
criteria.from(Message.class);
return entityManager.createQuery(criteria).getResultList();
}
class MessageRestTest
class MessageRESTTest {
#InjectMocks
MessageREST messageREST;
#Mock
private EntityManager entityManager;
#Mock
private MessageRepository messageRepository;
#BeforeEach
void Mockactivator() {
MockitoAnnotations.initMocks(this);
}
#Test
void testgetlistall()
{
List<Message> messageList = new ArrayList<>();
Message message = new Message();
messageList.add(message);
when(messageRepository.getAll()).thenReturn(messageList);
messageList = messageREST.getListAll();
}
}
The Error am getting is NullpointerException and it's comming from the last line "messageList = messageREST.getListAll()"
Thanks in advance!
It might not what you are looking for, but just in case:
Usually for such repository tests in memory databases are used, for example H2. So you can fill it with test data and then call getAll() to check if it works correctly.

JUnit Test Constructor Injection of List of Dependencies

I'm trying to write tests for the following class, where map fields are being injected through constructor injection with an argument list of dependencies. How can I mock the dependencies?
#Component
public class ComponentInputValidator {
private final Map<String, FaceInterface> faceMap;
private final Map<String, ArmsInterface> armsMap;
private final Map<String, MobilityInterface> mobilityMap;
private final Map<String, MaterialInterface> materialMap;
private final RobotComponentStock robotComponentStock;
public ComponentInputValidator(List<MaterialInterface> materialList,
List<FaceInterface> faceList,
List<ArmsInterface> armsList,
List<MobilityInterface> mobilityList,
RobotComponentStock robotComponentStock){
this.faceMap = faceList.stream().collect(Collectors.toMap(faceInterface -> faceInterface.getCode().name(), Function.identity()));
this.armsMap = armsList.stream().collect(Collectors.toMap(armsInterface -> armsInterface.getCode().name(), Function.identity()));
this.mobilityMap = mobilityList.stream().collect(Collectors.toMap(mobilityInterface -> mobilityInterface.getCode().name(), Function.identity()));
this.materialMap = materialList.stream().collect(Collectors.toMap(materialInterface -> materialInterface.getCode().name(), Function.identity()));
this.robotComponentStock = robotComponentStock;
}
public boolean validateStockAvailability(RobotComponent robotComponent){
String face = robotComponent.getFace();
String arms = robotComponent.getArms();
String mobility = robotComponent.getMobility();
String material = robotComponent.getMaterial();
Code faceCode = faceMap.get(face).getCode();
Code armsCode = armsMap.get(arms).getCode();
Code mobilityCode = mobilityMap.get(mobility).getCode();
Code materialCode = materialMap.get(material).getCode();
if (robotComponentStock.getQuantity(faceCode)<1 ...{
...
return false;
}
return true;
}
}
FaceInterface, ArmsInterface, MobilityInterface, MaterialInterface are interfaces that have different implementations.
What I tried:
#MockBean
private RobotComponentStock robotComponentStock;
#MockBean
private List<FaceInterface> faceInterfaceList;
#MockBean
private List<MobilityInterface> mobilityInterfaceList;
#MockBean
private List<ArmsInterface> armsInterfaceList;
#MockBean
private List<MaterialInterface> materialInterfaceList;
#InjectMocks
private ComponentInputValidator componentInputValidator;
Got an error:
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'componentInputValidator' of type 'class com.demo.robot_factory.service.ComponentInputValidator'.
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
for the line
faceList.stream().collect(Collectors.toMap(faceInterface -> faceInterface.getCode().name(), Function.identity()));
You are mixing two different concepts in your test.
#MockBean is a Spring annotation used in Integration Tests. It will create a mock and Spring's normal injection mechanism will inject it into your Bean.
#InjectMock on the other hand is an annotation from Mockito used in Unit Tests.
I can recommend this Blog Post on the Subject:
#Mock vs. #MockBean When Testing Spring Boot Applications
If you want to write a Unit Test I would suggest swapping all the #MockBean annotations with Mockitos #Mock.
// same for the other dependencies you want to mock
#Mock
private List<MaterialInterface> materialInterfaceList;
#InjectMocks
private ComponentInputValidator componentInputValidator;
This should fix the exception.
Looking at your code I would suggest a totally different approach for your test.
I don't understand why you want to mock the Lists in the first place. Can't you just instantiate the Lists and construct your ComponentInputValidator by hand? For example:
#Test
void test(){
List<MaterialInterface> materialList = List.of(...)
//initialize your other dependencies here
//pass all Lists into the constructor
var validator = new ComponentInputValidator(materialList,...)
//make your assertions on the validator
}

New to Java. Need help to understand testing for my function with some dependencies

#Autowired
private Publisher Publisher;
private int Id = 12345;
private BClient bClient = new BClient(Id);
private Map<Integer, Boolean> defaultBooleanValueMap;
private LoadCache<Integer, Boolean> booleanCache = CacheBuilder.newBuilder()
.refreshAfterWrite(refreshRate, TimeUnit.SECONDS)
.build(
new CacheLoader<Integer, Boolean>() {
#Override
public Boolean load(Integer id) throws Exception {
return fetchBooleanValue(id);
}
}
);
private boolean fetchBooleanValue(int id) {
long fetchStart = System.currentTimeMillis();
boolean val = bClient.getBoolean(id, defaultBooleanValueMap.get(id));
publisher.publish(
publisher.getDistributionMetric("fetchtime.bool", System.currentTimeMillis() - fetchStart));
return val;
}
public boolean getBoolean(int id) {
return booleanCache.getUnchecked(id);
}
//Trying to test getBoolean(int id) function. I'm mocking bClient, Publisher. Not sure how to properly test it
// Could anyone help me understand how to test it
//testing with
SomeClass someClass = new SomeClass();
#Mock
Publisher publisher;
#Mock
BClient bClient;
#Test
public void testGetBoolean(){
bClient = new BClient(12345);
Map<Integer,Boolean> defaultBooleanValueMap = null;
defaultBooleanValueMap.put(123, false);
when(bClient.getBoolean(123,
defaultBooleanBregValueMap.get(123))).thenReturn(false);
boolean b = someClass.getBoolean(123);
assertFalse(b);
}
// i'm don't know if i'm doing it right
Are you using Mockito?
It's good practice to not start a field name with a capital (Publisher for instance)
Personally i think it will be better to make all these methods protected instead of private, so that you can test each of them separately.
however this would be an example of a unit test for your code.
You can use Mockito to check if certain method calls are fired the amount of time you expect them to be fired.
I did not include all but you can just add if you need more tests.
Further i recommend to read about Mockito as it has some really powerful unit test tools
#Test
public void testGetBoolean () {
xxx.getBoolean
//the following line can only be done if you spy your service
Mockito.verify(xxx, times(1)).fetchBooleanValue(any());
//this line can be done if you mock bClient
Mockito.verify(bClient , times(1)).getBoolean(any(), any()); //Mockito.any() or you can fill in the real values if you really want.
//this line can be done if you mock Publisher
Mockito.verify(publisher, times(1)).publish(any); //again any or the real value you want to pass
}
I just now saw your unit tests, you can inject the mocks in you class with the following anotatation:
#InjectMocks
SomeClass someClass;
when mocking a class you don't manually have to create it again.
You don't have to mock the Bclient as you already create it with "new Bclient" instead of autowiring it.
I feel the #InjectMocks is not working because you didn't tell Spring that your class is a service component.
#Service
public class SomeClass {
//insert code here
}

Spring Test Junit throws null pointer exception

I have this code for testing :
private static final Integer documentSetId = 1143;
private static final Integer dsLifeCycleStateId = 1;
private static final String dsLifecycleState = "CVS_CREATED";
CVBusiness cvBusinessTest;
DocumentService documentService;
DocumentSetRepository documentSetRepository;
private DocumentSet documentSet;
private DSLifeCycleState dsLifeCycleState;
#Before
public void setUp(){
cvBusinessTest = new CVBusinessImpl();
documentService = mock(DocumentService.class);
documentSetRepository = mock(DocumentSetRepository.class);
documentSet = new DocumentSet();
dsLifeCycleState = new DSLifeCycleState();
documentSet.setActive(true);
documentSet.setDocumentSetId(documentSetId);
documentSet.setMarkedForTranslation(false);
dsLifeCycleState.setDsLifeCycleStateId(dsLifeCycleStateId);
dsLifeCycleState.setLabel(dsLifecycleState);
documentSet.setDsLifeCycleState(dsLifeCycleState);
when(documentService.getDocumentSetById(documentSetId)).thenReturn(documentSet);
when(documentService.updateDocumentSet(documentSet)).thenReturn(documentSet);
when(documentSetRepository.findOne(documentSetId)).thenReturn(documentSet);
}
#Test
public void markedForTranslationTest() {
boolean retValue = true;
DocumentSet docSet = documentService.getDocumentSetById(documentSetId);
dsLifeCycleState = docSet.getDsLifeCycleState();
if (dsLifeCycleState.getLabel().equals(LifeCycleStateEnum.CVS_CREATED.message()) && docSet.isActive() && !docSet.isMarkedForTranslation() && ((docSet.getfDR() == null) || docSet.getfDR().equals(""))){
documentSet.setMarkedForTranslation(true);
retValue = documentService.updateDocumentSet(documentSet) != null;
}
// retValue = cvBusinessTest.markedForTranslation(documentSetId);
assertTrue(retValue);
}
and when i run Junit, it finished with errors:
java.lang null pointer exception.
Which error points the method bellow
DocumentSet documentSet = documentService.getDocumentSetById(id)
which is in CVBusiness package extended by CVBusinessImpl.
My question is why documentService in CVBusinessImpl throws Null exception?
Thanks!
Maybe you missed to use Spring in your Test? How the documentService is injected?
Add #RunWith(SpringJUnit4ClassRunner.class) to your test class to enable Spring in the test. If you use autowire that should be all. You may also need a #ContextConfiguration annotation to your test and/or add #Resource to the members to be injected.
In your setup, you create your class to test:
cvBusinessTest = new CVBusinessImpl();
and one of the services it requires:
documentService = mock(DocumentService.class);
but you never wire them together. So when your CVBusinessImpl implementation calls:
DocumentSet documentSet = documentService.getDocumentSetById(id)
documentService is still null.
You should wire your objects before testing, either through using a Spring test runner or through setting that field. Something in your setup method like:
cvBusinessTest.setDocumentService(documentService);

Categories