-1

I was trying to mock a private method's output which is being called inside another private method, I have no choice but to test the later private method, so I have added sample test code which I can represent here,

This Sample Class

package com.testableClass;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class TestableClass {
    
     private int initialMockMethod(Object obj)
     {
         
         System.out.println(" ++++ Came Here ++++ ");
         
         String str = getRestString("");
         
         System.out.println("str ="+str);
         return str.length();
     }

     private String getRestString(String abc)
     {
         String output="";
         try {

                URL url = new URL("https://gorest.co.in/public-api/users");//your url i.e fetch data from .
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Accept", "application/json");
                if (conn.getResponseCode() != 200) {
                    throw new RuntimeException("Failed : HTTP Error code : "
                            + conn.getResponseCode());
                }
                InputStreamReader in = new InputStreamReader(conn.getInputStream());
                BufferedReader br = new BufferedReader(in);
                
                while ((output = br.readLine()) != null) {
                    System.out.println(output);
                }
                conn.disconnect();
                
                return output;

            } catch (Exception e) {
                System.out.println("Exception in NetClientGet:- " + e);
            }
        return abc;
     }
     
}

Now This PowerMock Class

package com.testableClass;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

import junit.framework.Assert;

@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.testableClass.TestableClass")
public class PowerMockTest {
    

    
    @Test
    public void testPrivateMethod() throws Exception
    {
        String message = "Hello PowerMockito";
        String expectation = "Expectation";
 
        TestableClass mock = PowerMockito.spy(new TestableClass());
//      PowerMockito.doReturn(expectation).when(mock, "getRestString", message);
        PowerMockito.when(mock, "getRestString", message).thenReturn(expectation);
        int count = Whitebox.invokeMethod(mock, "initialMockMethod", new Object());
       System.out.println(" +++ Count : "+count+" ++++ ");
        Assert.assertTrue(true);
    }

}

my issue is when I am running my test case then

  PowerMockito.when(mock, "getRestString", message).thenReturn(expectation);

executes original method and returns original output while my requirement is that, when my test case is actually calling private method initialMockMethod it should not call getRestString instead of that it should return my mocked expected output which is "Expectation"

1 Answers1

0

Instead of using reflection and PowerMock, I'd say that do not try to mock a private method. It is a hidden detail of the class.

What I suggest is that, if your method makes an HTTP request, then let it do that. But you can use a mock server to mock the response. But to do that, you need to make your endpoint external, so that it can be injected.

I changed your class a little bit; still the same purpose tho.

public class TestableClass {
    private final String resourceUrl;

    public TestableClass(String resourceUrl) {
        this.resourceUrl = resourceUrl;
    }

    public int publicMethod() {
        return initialMockMethod(null);
    }

    private int initialMockMethod(Object obj) {
        var str = getRestString("");
        return str.length();
    }

    private String getRestString(String abc) {
        try {
            var url = new URL(resourceUrl);
            var conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "application/json");
            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("Failed : HTTP Error code : "
                        + conn.getResponseCode());
            }

            var in = new InputStreamReader(conn.getInputStream());
            var br = new BufferedReader(in);

            var result = br.lines().collect(Collectors.joining("\n"));
            conn.disconnect();

            return result;
        } catch (Exception e) {
            System.out.println("Exception in NetClientGet:- " + e);
        }
        return abc;
    }

}

and here's the test

public class TestingTestableClass {

    @Test
    @SneakyThrows
    public void test() {
        final var server = new MockWebServer();
        server.start(9999);

        var instance = new TestableClass("http://127.0.0.1:9999");

        server.enqueue(
                new MockResponse()
                    .setResponseCode(200)
                    .setBody("this is the response")
        );

        final int result = instance.publicMethod();

        Assertions.assertEquals(
                "this is the response".length(),
                result
        );

        final var record = server.takeRequest();
        final var method = record.getMethod();
        Assertions.assertEquals("GET", method);

        server.shutdown();
    }

}

Check out MockWebServer here

Mansur
  • 1,661
  • 3
  • 17
  • 41
  • I got your point here but my challange is tthe change u did I can not do as my structure is defined already where I have been asked to test a private method – Atul Singh Rathore Aug 09 '20 at 14:44
  • now in that private method another private method is being called which internally calls multiple other private method and data base calls to produce result – Atul Singh Rathore Aug 09 '20 at 14:44
  • Now my this code is just a representation that getString method is a just representation of a complex private method which is being called by another private method and this caller method I have to tes using whitebox – Atul Singh Rathore Aug 09 '20 at 14:45
  • Here whatever changes u did is ok for this scenario but its merely representation of some other properitery code which I can not post here – Atul Singh Rathore Aug 09 '20 at 14:47
  • So my doubt is still there that can I mock internal private tire two methods while testing private tier 1 method of same class – Atul Singh Rathore Aug 09 '20 at 14:47
  • here tier 1 is initialMockMethod while tier 2 or level 2 method I am trying to say to getString which is called internally and contains complex logics using database and rest calles – Atul Singh Rathore Aug 09 '20 at 14:49
  • The idea was if I can do a mock of a private complex method of the same class of which the method I am testing is calling it then I can bypass all those injection Datasource Connection creation and rest calls – Atul Singh Rathore Aug 09 '20 at 14:50
  • otherwise, I have to mock each and every external dependency one by one and their results to test the class and all these because of one method of same class I can not mock – Atul Singh Rathore Aug 09 '20 at 14:51
  • @AtulSinghRathore you can move some of those private methods to another class since I believe this class is doing too much. You can then add that class as a dependency and you can mock that. – Mansur Aug 09 '20 at 15:08