Mockito mock the constructor based api call invocation - spring

I have the following service which has the executeSofortRequest which makes a call to the third party api
#RequiredArgsConstructor
public class SofortRequestService {
public com.sofort.lib.payment.products.response.PaymentResponse executeSofortRequest(com.sofort.lib.payment.products.request.PaymentRequest sofortRequest,
ExternalPaymentInfoEntity externalPaymentInfo) {
com.sofort.lib.payment.products.response.PaymentResponse sofortResponse;
try {
sofortResponse = new DefaultSofortLibPayment(customerId, apiKey).sendPaymentRequest(sofortRequest);
} catch (HttpAuthorizationException e) {
saveExternalPaymentInfo(externalPaymentInfo, e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, SOFORT_AUTHORIZATION_FAILED_WITH_GIVEN_APIKEY, e);
} catch (ConnectionException e) {
saveExternalPaymentInfo(externalPaymentInfo, e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, SOFORT_COMMUNICATION_FAILED, e);
}
return sofortResponse;
}
}
Now I have mocked this in my test
#RunWith(MockitoJUnitRunner.class)
public class SofortRequestServiceTest {
#Mock
private ExternalPaymentInfoRepository externalPaymentInfoRepository;
#InjectMocks
private SofortRequestService sofortRequestService;
#Test
public void test_executeSofortRequest() throws JsonProcessingException {
given(new DefaultSofortLibPayment(1234, "test-api-key").sendPaymentRequest(sofortPaymentRequest)).willThrow(HttpAuthorizationException.class);
//When
assertThatThrownBy(() -> sofortRequestService.executeSofortRequest(sofortPaymentRequest, externalPaymentInfoEntity))
.isInstanceOf(HttpAuthorizationException.class);
//Then
verify(externalPaymentInfoRepository, times(1))
.save(ExternalPaymentInfoEntity.builder()
.referenceTransaction(paymentRequest.getTransactionId())
.customerId(paymentRequest.getPaymentDocument()
.getCustomer()
.getCustomerId())
.eventType(OUTGOING)
.paymentType("sofort checkout")
.action(AUTH)
.requestData(new ObjectMapper().writeValueAsString(paymentRequest))
.success(false)
.build());
}
}
My problem is that when the test execute and runs line
given(new DefaultSofortLibPayment(1234, "test-api-key").sendPaymentRequest(sofortPaymentRequest)).willThrow(HttpAuthorizationException.class);
it is running the actual implementation and not the mock and then it fails to exuecute further.
How can I write an integration test for executeSofortRequest method

This may not be the answer you want,
but in your situation I would add a package access zero (0) parameter constructor
as a way to support mocking during unit tests.
In this situation,
make sure that the unit test is in the same package as the class it is testing.

Related

thenThrow() not throwing an exception

I have a method in OneServiceImpl class as follows. In that class I am calling an interface method from another class.
public class OneServiceImpl {
//created dependency
final private SecondService secondService;
public void sendMessage(){
secondService.validateAndSend(5)
}
}
public interface SecondService() {
public Status validateAndSend(int length);
}
public class SecondServiceImpl {
#Override
public Status ValidateAndSend(int length) {
if(length < 5) {
  throw new BadRequestException("error", "error");
}
}
}
Now when I am try to perform unit test on OneServiceImpl I am not able to throw a BadRequestException.
when(secondService.validateAndSend(6)).thenThrow(BadRequestException.class);
Not quite sure what your use case is, but I think you should write an own test to accept and test an exception.
#Test(expected = BadRequestException.class)
public void testValidateAndSend(){
SecondService secondService = new SecondService();
secondservice.ValidateAndSend(6); //method should be lowercase
}
Not sure this is the case considering you didn't post a full example of code + unit tests, but your mock will throw only when you are passing 6 as parameter. When configuring the behaviour of your mock with when you are telling it to throw only when the validateAndSend method is called with parameter 6.
when(secondService.validateAndSend(6)).thenThrow(...)
In your code you have 5 hardcoded. So that mock will never throw for the code you have, because it's configured to react to an invocation with parameter 6 but the actual code is always invoking it passing 5.
public void sendMessage(){
secondService.validateAndSend(5)
}
If the value passed to the mock is not important you could do something like the following, that will throw no matter what's passed to it:
when(secondService.validateAndSend(any())).thenThrow(BadRequestException.class);
On the other hand, if the value is important and it has to be 5 you could change the configuration of your mock with:
when(secondService.validateAndSend(5)).thenThrow(BadRequestException.class)

Errors: UnfinishedStubbing

I am writing Junit test case and I want to mock KafkaTemplate method kafkaTemplate.send(TOPIC_NAME, "someData");. In my project, I am using spring boot and Kafka.
Below is the StudentRecords class. I am using mockito for mocking the dependencies.
#Component
public class StudentRecords {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#Value("${topicNameForStudents}")
private String TOPIC_NAME;
public String sendStudentData(StudentDTO studentDTO) {
String studentStr = null;
try {
if(null == studentDTO) {
throw new StudentException("studentDTO Object cant be null");
}
if(studentDTO.getId() == null) {
throw new StudentException("Id cant be empty");
}
ObjectMapper mapper = new ObjectMapper();
studentStr = mapper.writeValueAsString(srvgExecution);
kafkaTemplate.send(TOPIC_NAME, studentStr);
return "SUCCESS";
} catch (JsonProcessingException e) {
e.printStackTrace();
return "ERROR";
}
}
}
And test class is as follows:
#ExtendWith({ SpringExtension.class, MockitoExtension.class })
class StudentRecordsTest {
#InjectMocks
StudentRecords studentRec;
#Mock
private KafkaTemplate<String, String> kafkaTemplate;
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
}
}
And I getting the following error
[ERROR] Errors:
[ERROR] studentRec.testSendStudentData: » UnfinishedStubbing
It is happening at line studentRec.sendStudentData(studentDTO);
How I can resolve/write the junit for this?
#Test
void testSendStudentData() {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("ABC");
studentDTO.setAddress("Some Address");
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
studentRec.sendStudentData(studentDTO);
Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
}
after updating the junit to above one, ended up with below error at this statement Mockito.verify(kafkaTemplate).send(Mockito.anyString(), Mockito.anyString());
Argument(s) are different! Wanted:
kafkaTemplate.send(
<any string>,
<any string>
);
Your mock statement is incomplete.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString()));
KafkaTemplate's send method returns a ListenableFuture object and hence you need to have your mock return it.
I understand, you are not really using the returned value as part of your code.
In that case you may simply return null as below.
Mockito.when(kafkaTemplate.send(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
Although, I would suggest you should ideally check for return value for better error handling, but that can be a different story altogether.
In case you do plan to handle error by checking the return value, your above mock statement can be written to return both success and fail cases.
You may check below thread for more details on how to set the correct mock expectations for KafkaTemplate.
How to mock result from KafkaTemplate

Why isnt Mockito doThrow throwing an Exception in this case? zero interactions with mock

method I am testing (the method setEventHubDataPayload throws JSONException and JsonProcessingException):
public class EventHubMapper {
//inits
public byte[] toEventDataJsonByteArray(UserRecord inbound) {
EventHubDto ehDto = new EventHubDto();
ehDto.setEventTypeVersion(inbound.getVersion());
ehDto.setEventId(inbound.getNotificationId());
JSONObject eventJson = new JSONObject(ehDto);
try {
eventJson.put("data", setEventHubDataPayload(ehDto, inbound));
} catch (JSONException e) {
analytics.trackError(AnalyticsConstants.EventHub.JSON_MAPPING_ERROR, e.toString());
} catch (JsonProcessingException e) {
analytics.trackError(AnalyticsConstants.EventHub.JSON_PROCESSING_ERROR, e.toString());
}
return eventJson.toString().getBytes();
}
}
unit test code:
#Test
public void toEventDataByteArray_JsonException() throws JSONException, JsonProcessingException {
EventHubMapper ehmMock = Mockito.spy(eventHubMapper);
doThrow(new JSONException("blah")).when(ehmMock).setEventHubDataPayload(any(), any());
eventHubMapper.toEventDataJsonByteArray(setUpMockUserRecord());
verify(analytics, times(1)).trackError( AnalyticsConstants.EventHub.JSON_MAPPING_ERROR, new JSONException("blah").toString());
}
I've tried using more specific matchers ... ex: any(EventHubDto.class) or any(UserRecord.class) and got the same result:
Wanted but not invoked:
analytics.trackError(
"EventHub_Publish_Error",
""
;
and also
Actually, there were zero interactions with this mock.
what is going on here?
I think you need to call like below while testing.
ehmMock.toEventDataJsonByteArray(setUpMockUserRecord());

how to let spring data mongodb to execute createIndexes before every test method?

A field of MongoDB Entity MyCardDO, explicitly set it to unique
#Indexed(unique=true)
private String uid;
and there is a MyCardService to crud MyCardDO, and there is a MyCardServiceTest to test MyCardService, there is a add_repeat_uid_record_failed inner MyCardServiceTest to test the uid cannot be duplicated,
MyCardDO myCardDO1 = new MyCardDO();
myCardDO1.setUid("1");
myCardService.add(myCardDO1);
try {
MyCardDO myCardDO2 = new MyCardDO();
myCardDO2.setUid("1");
myCardService.add(myCardDO2);
Assert.fail();
} catch (DuplicateKeyException e) {
assertTrue(e.getMessage().contains("E11000 duplicate key error collection: opportunity-test.pro_mycard index: uid dup key: { : \"1\" }"));
}
If I run this test method directly it's OK, but I run the whole MyCardServiceTest this method is failed, and from Wireshark I know the createIndexes only executed once, if dropped the collection it will not createIndexes again
#After
public void tearDown() {
mongoTemplate.dropCollection(MyCardDO.class);
}
So how to let spring to execute createIndexes before every test method? that is
#Before
public void setUp() {
// how to auto execute createIndexes before every test method
// prepare some test data
myCardService.add(myCardDO1);
}
p.s.
#RunWith(SpringRunner.class)
#DataMongoTest(includeFilters = #ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE,value={MyCardService.class}))
#ActiveProfiles("test")
#Import(SpringMongoConfig.class)
public class MyCardServiceTest {
//...
}
Wireshark screenshot
Final my resolution :
#After
public void tearDown() {
mongoTemplate.remove(new Query(), MyCardDO.class);
}
#AfterClass
public static void finalClean() {
mongoTemplate.dropCollection(MyCardDO.class);
}
that is after finished every test method only delete all records and at final when the whole test class is finished to drop the collection.

ClassCastException when using embedded glassfish for unit tests

I'm running some unit tests on some EJBS via maven and an embedded glassfish container. One of my tests works, but all subsequent attempts to test a different EJB result in the same error:
java.lang.ClassCastException: $Proxy81 cannot be cast to
Followed by whatever bean I'm attempting to test. I'm confident my setup is good since, as I say, one of my beans can be tested properly.
Examples of workiing code:
#Stateful
public class LayoutManagerBean implements LayoutManager {
private final Log LOG = LogFactory.getLog(LayoutManagerBean.class);
public List<Menu> getMenus(User currentUser) {
...
}
}
#Local
public interface LayoutManager {
public List<Menu> getMenus(User user);
}
And the test:
public class LayoutManagerTest {
private static EJBContainer ejbContainer;
private static Context ctx;
#BeforeClass
public static void setUp() {
ejbContainer = EJBContainer.createEJBContainer();
ctx = ejbContainer.getContext();
}
#AfterClass
public static void tearDown() {
ejbContainer.close();
}
#Test
public void getMenus() {
LayoutManager manager = null;
try {
manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
} catch (NamingException e) {
System.out.println("Failed to lookup the gosh darned bean!");
}
assertNotNull(manager);
//Menu[] menus = manager.getMenus();
//assertTrue(menus.length > 1);
}
}
And an example of a failure:
#Singleton
public class OpenChurchPortalContext implements PortalContext {
private Set<PortletMode> portletModes = Collections.emptySet();
private Set<WindowState> windowStates = Collections.emptySet();
private Properties portalProperties = new Properties();
public OpenChurchPortalContext() {
portletModes.add(PortletMode.VIEW);
portletModes.add(PortletMode.HELP);
portletModes.add(PortletMode.EDIT);
portletModes.add(new PortletMode("ABOUT"));
windowStates.add(WindowState.MAXIMIZED);
windowStates.add(WindowState.MINIMIZED);
windowStates.add(WindowState.NORMAL);
}
...
}
And the test:
public class OpenChurchPortalContextTest {
private static EJBContainer ejbContainer;
private static Context ctx;
#BeforeClass
public static void setUp() {
ejbContainer = EJBContainer.createEJBContainer();
ctx = ejbContainer.getContext();
}
#AfterClass
public static void tearDown() {
ejbContainer.close();
}
#Test
public void test() {
OpenChurchPortalContext context = null;
try {
context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
} catch (NamingException e) {
System.out.println("Failed to find the bean in the emebedded jobber");
}
assertNotNull(context);
Set<PortletMode> modes = (Set<PortletMode>) context.getSupportedPortletModes();
assertTrue(modes.size() > 1);
Set<WindowState> states = (Set<WindowState>) context.getSupportedWindowStates();
assertTrue(states.size() > 1);
}
}
Any ideas as to why this may not be working?
You often get this problem if you are proxying a class, not an interface. Assuming that it's this line which is failing:
context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
OpenChurchPortalContext is a class, but it is being wrapped by a proxy class to implement the EJB specific functionality. This proxy class isn't a subclass of OpenChurchPortalContext, so you're getting a ClassCastException.
You aren't getting this with the first example, because the LayoutManager is an interface.
LayoutManager manager = null; // INTERFACE, so it works
try {
manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
} catch (NamingException e) {
System.out.println("Failed to lookup the gosh darned bean!");
}
First, you can test to see if this is really your problem, change context to be a PortalContext not OpenChurchPortalContext:
PortalContext context = null;
try {
context = (PortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
} catch (NamingException e) {
System.out.println("Failed to find the bean in the emebedded jobber");
}
If your problem really is the Proxy, then the above code should work. If this is the case, you have two potential solutions:
When you do the ctx.lookup, always use an interface. This can be a bit of a pain, because you need to define an interface specifically for each EJB.
You may be able to configure your EJB container to proxy the classes instead of just the interfaces, similar to proxyTargetClass for Spring AOP. You'll need to check with the documentation for your container for that.
Your singleton EJB has a default local business interface by means of implementing PortalContext interface. The test client should know it only by its business interface, and the actual bean class (OpenChurchPortalContext) should not be referenced directly by the client. So the fix is to look it up by its business interface PortalContext.

Resources