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
Related
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
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
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.
I don't know if this is possible with Powermock.
I need to use Powermock to mock a private method that is called in the constructor of the class that I need to test.
So I have a test class like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest(XMLTransaction.class)
public class CloseSummaryOrCloseTrailerResponseTest {
public final static String URL="WL_APPSERVER";
private XMLTransaction xmlTransaction;
#Before
public void initMocks() throws Exception {
xmlTransaction = PowerMockito.spy(new XMLTransaction(URL));
PowerMockito.doAnswer(new org.mockito.stubbing.Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return null; //does nothing
}
}).when(xmlTransaction.getClass(), "initialize");
PowerMockito.doNothing().when(xmlTransaction.getClass(), "initialize");
}
#Test
public void whenCloseSummaryResponseNoErrorExpectCorrectXmlMsgProduced () throws Exception
{
//set the mock object here
try {
String actualXmlScannerMsg = xmlTransaction.closeSummaryResponseToXMLNoErrors(mockCloseTrailerResponse);
Assert.assertNotNull(actualXmlScannerMsg);
Assert.assertEquals(msgNoCarReturnCharCloseSummaryResponse, actualXmlScannerMsg);
}
catch(JsonProcessingException jEx)
{
Assert.fail("JsonProcessingException: " + jEx.getMessage());
}
catch(Exception ex)
{
Assert.fail("Exception occurred: " + ex.getMessage());
}
}
}
I get a null pointer exception when creating the spy.
The constructor new XMLTransaction(URL) calls the initialize method which is the method I want to do nothing.
Is there any way to get around this problem. If I use the default constructor, the class is not created.
I figured this out...
I created a default constructor and set all of the classes instantiated in the initialize method to null.
Removed this from initMock()
PowerMockito.doAnswer(new org.mockito.stubbing.Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return null; //does nothing
}
}).when(xmlTransaction.getClass(), "initialize");
PowerMockito.doNothing().when(xmlTransaction.getClass(), "initialize");
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