Mock private method using PowerMockito - java

I'm using PowerMockito to mock the private method call (privateApi) but it still makes the privateApi call which in turn makes another thirdPartCall. I'm getting into problem when thirdPartyCall throws exception. As far as I understand, if I'm mocking the privateApi, it shouldn't get into method implementation detail and return the mock response.
public class MyClient {
public void publicApi() {
System.out.println("In publicApi");
int result = 0;
try {
result = privateApi("hello", 1);
} catch (Exception e) {
Assert.fail();
}
System.out.println("result : "+result);
if (result == 20) {
throw new RuntimeException("boom");
}
}
private int privateApi(String whatever, int num) throws Exception {
System.out.println("In privateAPI");
thirdPartyCall();
int resp = 10;
return resp;
}
private void thirdPartyCall() throws Exception{
System.out.println("In thirdPartyCall");
//Actual WS call which may be down while running the test cases
}
}
Here is the test case:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClient.class)
public class MyclientTest {
#Test(expected = RuntimeException.class)
public void testPublicAPI() throws Exception {
MyClient classUnderTest = PowerMockito.spy(new MyClient());
PowerMockito.when(classUnderTest, "privateApi", anyString(), anyInt()).thenReturn(20);
classUnderTest.publicApi();
}
}
Console trace:
In privateAPI
In thirdPartyCall
In publicApi
result : 20

You just need to change the mock method call to use doReturn.
Example Partial Mocking of Private Method
Test code
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClient.class)
public class MyClientTest {
#Test(expected = RuntimeException.class)
public void testPublicAPI() throws Exception {
MyClient classUnderTest = PowerMockito.spy(new MyClient());
// Change to this
PowerMockito.doReturn(20).when(classUnderTest, "privateApi", anyString(), anyInt());
classUnderTest.publicApi();
}
}
Console trace
In publicApi
result : 20

Related

null exception for mocked service

I want to mock and test this Java code:
public class BusterClient {
public BusterClient() {
}
#Autowired
public BusterClient(PromoCodeService promoCodeService) {
this.promoCodeService = promoCodeService;
}
private Optional<PromoCode> getPromoCode(CustomerRegistrationEvent event) {
Optional<Long> campaignId = getCampaignIdAsLong(event);
if (!event.hasPromoCode() || !campaignId.isPresent()) {
return Optional.empty();
}
return promoCodeService.findByCampaignPromoCodeIds(campaignId.get(), event.getPromoCode());
}
}
I created this test code:
#InjectMocks
private BusterClient app = new BusterClient();
#Test
public void testTrackedReferralNotMatchingPromoCode() throws Exception {
PromoCodeService o = mock(PromoCodeService.class);
when(o.findByCampaignPromoCodeIds(Long.valueOf(12), "test")).thenReturn(Optional.of(PromoCode.builder().id(Long.valueOf(1)).build()));
try {
Method method = BusterClient.class.getDeclaredMethod("getPromoCode", CustomerRegistrationEvent.class);
method.setAccessible(true);
method.invoke(app, customerRegistrationEvent);
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
But I get error: promoCodeService.findByCampaignPromoCodeIds(java.lang.Long, String)" because "this.promoCodeService" is null
Do you know what is the proper way to mock PromoCodeService service?
You must do something like this
#Mock
private PromoCodeService promoCodeService;
#InjectMocks
private BusterClient app;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testTrackedReferralNotMatchingPromoCode() throws Exception {
when(promoCodeService.findByCampaignPromoCodeIds(Long.valueOf(12), "test")).thenReturn(Optional.of(PromoCode.builder().id(Long.valueOf(1)).build()));
...
// Test a public method and not a private method. Use one public method that in turn will call your "getPromoCode".
}
Try to inject your mocked object into the client you are creating, and create the client in the test method instead of making it class variable
var app = new BusterClient(o); //where o is the object you created with mockito

mocking a recursive class

using Spring 2.0.3.RELEASE, JUnit Jupiter 5.7.0, Mockito 3.3.3
try to test method method01 of class Class01:
public class Class01 {
private RestConnector con;
public Class01(){
con = RestConnector.getInstance();
}
public Response method01(String x) {
Class01 example = new Class01();
String x = example.isAuthenticated();
// more stuff after this
}
public String isAuthenticated() throws IOException {
// I do stuff
return "a string";
}
}
In the test class have tried
public class Class01Test{
#Mock private Class01 class01Mock;
#Spy #InjectMocks private Class01 class01;
#Test
public void test() throws Throwable {
doReturn("I returned").when(class01). ??? stuck here .. always goes into the isAuthenticated method
Response result = class01.method01("a string");
}
}
Currently the test is always running the real method isAuthenticated.
How to setup a mock for the field example in method method01 so that the execute skips going into method isAuthenticated?
try to test method method01 of class Class01
Then you don't need mocks.
#Test
public void test() throws Throwable {
Class01 c = new Class01();
Response expected = ... ;
assertEquals(c.method01("input"), expected);
}
If you want to inject behaviour into example variable, you need to move it to a field
public class Class01 {
private RestConnector con;
private Class01 inner;
public Class01(){
con = RestConnector.getInstance();
}
Class01(Class01 c) {
this();
this.inner = c;
}
public Response method01(String x) {
String x = inner.isAuthenticated();
// more stuff after this
}
Along with a Mock
#RunWith(MockitoJunitRunner.class)
public class Class01Test{
#Mock Class01 class01Mock;
#Test
public void test() throws Throwable {
Response expected = ... ;
when(class01Mock.isAuthenticated()).thenReture(expected); ... // TODO: Setup
Class01 c = new Class01(class01Mock); // pass in the mock
assertEquals(c.method01("input"), expected);
}
However, unclear why you need a nested object of the same class when you appear to only need this.isAuthenticated()
Ideally, you'd also mock the RestConnector

Mockito ArgumentCaptor doesn't capture argurment in catch block

I wrote this service
public class FirstService {
private final SecondService secondService;
public FirstService(SecondService secondService) {
this.secondService = secondService;
}
public void hz() throws Exception {
try {
methodThrowsException();
} catch (Exception e){
secondService.handleErrorMessage(e.getMessage());
throw e;
}
}
private void methodThrowsException() throws Exception {
throw new Exception("message");
}
}
And this service:
public class SecondService {
public void handleErrorMessage(String message) {}
}
I need to verify that handleErrorMessage was called. I wrote a test:
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class FirstServiceTest {
private FirstService firstService;
private SecondService secondService;
#Before
public void setUp() {
secondService = mock(SecondService.class);
firstService = new FirstService(secondService);
}
#Test(expected = Exception.class)
public void hz() throws Exception {
firstService.hz();
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(secondService).handleErrorMessage(argumentCaptor.capture());
String value = argumentCaptor.getValue();
assertEquals("message", value);
}
}
Test pass. But if I change assertEquals("message666", value); it still pass. If I don't throw an exception in catch block - ArgumentCaptor captures argument, but when I throw an exception it doesn't work.
Your test is annotated:
#Test(expected = Exception.class)
This means that the test will pass if an Exception (or any subclass of) makes it up to the top level. This happens on the first line of your test:
firstService.hz();
This is why it passes. Unfortunately, that exception means that the rest of your test is never run, since that exception propagates up and out of your test method.
A bit ugly, but this snippet does what you want:
#Test
public void hz() throws Exception {
try {
firstService.hz();
// If we get here, then we didn't throw an exception - fail
Assert.fail();
} catch (Exception ex) {
// Exception was expected - disregard and continue
// no-op
}
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(secondService).handleErrorMessage(argumentCaptor.capture());
String value = argumentCaptor.getValue();
assertEquals("message", value);
}
The above runs your method, and catches the exception (and fails if you didn't get the expected exception). Then, it proceeds, and runs the rest of your test.
JUnit 5 provides a slightly cleaner way, but you'd have to migrate:
#Test
public void hz() throws Exception {
Assertions.assertThrows(Exception.class, () -> firstService.hz());
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(secondService).handleErrorMessage(argumentCaptor.capture());
String value = argumentCaptor.getValue();
assertEquals("asdf", value);
}

How to mock private methods using PowerMockito? [duplicate]

I'm using PowerMockito to mock the private method call (privateApi) but it still makes the privateApi call which in turn makes another thirdPartCall. I'm getting into problem when thirdPartyCall throws exception. As far as I understand, if I'm mocking the privateApi, it shouldn't get into method implementation detail and return the mock response.
public class MyClient {
public void publicApi() {
System.out.println("In publicApi");
int result = 0;
try {
result = privateApi("hello", 1);
} catch (Exception e) {
Assert.fail();
}
System.out.println("result : "+result);
if (result == 20) {
throw new RuntimeException("boom");
}
}
private int privateApi(String whatever, int num) throws Exception {
System.out.println("In privateAPI");
thirdPartyCall();
int resp = 10;
return resp;
}
private void thirdPartyCall() throws Exception{
System.out.println("In thirdPartyCall");
//Actual WS call which may be down while running the test cases
}
}
Here is the test case:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClient.class)
public class MyclientTest {
#Test(expected = RuntimeException.class)
public void testPublicAPI() throws Exception {
MyClient classUnderTest = PowerMockito.spy(new MyClient());
PowerMockito.when(classUnderTest, "privateApi", anyString(), anyInt()).thenReturn(20);
classUnderTest.publicApi();
}
}
Console trace:
In privateAPI
In thirdPartyCall
In publicApi
result : 20
You just need to change the mock method call to use doReturn.
Example Partial Mocking of Private Method
Test code
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClient.class)
public class MyClientTest {
#Test(expected = RuntimeException.class)
public void testPublicAPI() throws Exception {
MyClient classUnderTest = PowerMockito.spy(new MyClient());
// Change to this
PowerMockito.doReturn(20).when(classUnderTest, "privateApi", anyString(), anyInt());
classUnderTest.publicApi();
}
}
Console trace
In publicApi
result : 20

How can I create unit test for void method with try...catch inside?

I create simple service for example:
public class MyService {
public void process() {
try {
CustomerMessagesService customerMessagesService = new CustomerMessagesService();
String message = customerMessagesService.getMessage();
// another logic which can throw an exception
SpamCenterService spamCenterService = new SpamCenterService();
spamCenterService.sendAdvertise(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
This service calls from Scheduler each 1 second.
customerMessagesService return message with an advertisement text or throw an exception if this message contains illegal text. If customerMessagesService return success text - it to send to spamCenterService and if customerMessagesService throw an exception - it exception just logged. There is another logic which can throw exception between calls of these services. This exception logs too.
Now I want to create a unit test for this method. But I don't understand how to test the void method with try..catch block.
I create this:
public class MyServiceTest {
private MyService myService;
#Before
public void setUp() {
myService = new MyService();
}
#Test
public void process() {
myService.process();
}
}
But it is always a success because the process method doesn't throw exceptions.
How can I test this method?
EDIT
I find one solution but I am not sure about it.
public class MyServiceImpl implements MyService {
public void process() {
try {
doIt();
} catch (Exception e) {
e.printStackTrace();
}
}
public void doIt() throws Exception {
CustomerMessagesService customerMessagesService = new CustomerMessagesService();
String message = customerMessagesService.getMessage();
// another logic which can throw an exception
SpamCenterService spamCenterService = new SpamCenterService();
spamCenterService.sendAdvertise(message);
}
}
And test doIt() method in MyServiceImpl.
In short do the following:
Move object creation out of your method
Create mocks (with e.g. Mockito) and inject those
verify that the mocks were used as expected
A concrete example below:
public class MyService {
private CustomerMessagesService customerMessagesService;
private SpamCenterService spamCenterService;
//inject dependencies
public MyService(CustomerMessagesService customerMessagesService, SpamCenterService spamCenterService) {
this.customerMessagesService = customerMessagesService;
this.spamCenterService = spamCenterService;
}
public void process() {
try {
String message = customerMessagesService.getMessage();
// another logic which can throw an exception
spamCenterService.sendAdvertise(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MyServiceTest {
private MyService myService;
// mock dependencies with Mockito
private CustomerMessagesService customerMessagesService = Mockito.mock(CustomerMessagesService.class);
private SpamCenterService spamCenterService = Mockito.mock(SpamCenterService.class);
#Before
public void setUp() {
myService = new MyService(customerMessagesService, spamCenterService);
}
#Test
public void process() {
myService.process();
Mockito.verify(customerMessagesService).getMessage();
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(spamCenterService).sendAdvertise(messageCaptor.capture());
assertThat(messageCaptor.getValue(), is(nullValue()));
}
#Test
public void processWithSpecificCustomerMessageServiceBehaviour() {
Mockito.given(customerMessagesService.getMessage()).willReturn("expectedString");
myService.process();
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
Mockito.verify(spamCenterService).sendAdvertise(messageCaptor.capture());
assertThat(messageCaptor.getValue(), is("expectedString"));
}
#Test
public void processCatchExceptions() {
Mockito.given(customerMessagesService.getMessage()).willThrow(new Exception("Bad message"));
myService.process();
// if exception is thrown then the code should not reach spamCenterService.sendAdvertise()
Mockito.verify(spamCenterService, Mockito.never()).sendAdvertise(Mockito.anyString());
}
}
Note that you can also setup mocks to throw specific exceptions or return specific values so that you can test all possible scenarios.

Categories

Resources