es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Burlándose de una conexión de URL utilizando PowerMockito

Estoy tratando de probar mi código que analiza una respuesta JSON de una URL.

Este es el código que estoy tratando de probar:

public String getJsonResponseFromUrl(String url) {
    JSONObject jsonObject = null;
    try {
        URL apiUrl = new URL(url);
        jsonObject = new JSONObject(IOUtils.toString(apiUrl, StandardCharsets.UTF_8));
    } catch (IOException e) {
        System.out.println(e);
    }
    return jsonObject.toString();
}

Este es el test Junit que he configurado:

import java.net.URL;
import org.json.JSONObject;

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({URL.class, Service.class, JSONObject.class})
public class ServiceTest {

    private JSONObject mockJsonObject;

    private URL mockUrl;

    @InjectMocks
    private Service mockService;

    @Before
    public void setUp() throws Exception {
        mockJsonObject = PowerMockito.mock(JSONObject.class);
        mockUrl = PowerMockito.mock(URL.class);
    }

    @Test
    public void testGetJsonResponseFromUrl() throws Exception {
        String url = "http://some-url.com";
        PowerMockito.whenNew(URL.class).withArguments(url).thenReturn(mockUrl);
        PowerMockito.whenNew(JSONObject.class).withAnyArguments().thenReturn(mockJsonObject);

        mockService.getJsonResponseFromUrl(url);
    }

}

El problema ocurre con el objeto URL simulado.

mockUrl = PowerMockito.mock(URL.class);

Cuando el método setUp llega a esa línea, obtengo este error en la consola:

java.lang.RuntimeException: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @593634ad

Según entiendo, PowerMockito permite simular clases finales como la clase URL.

No sé si hay algo que me estoy pasando por alto.

Versión de PowerMockito: 2.0.9
Versión de Junit: 4.12

Gracias.

EDITAR: Me doy cuenta de que la prueba en esta clase realmente no está probando nada (es decir, no hay afirmaciones). Hice esta prueba para verificar primero que todo se estuviera simulando correctamente.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Solución para probar el código que analiza una respuesta JSON de una URL

    Problema

    Estoy intentando probar el siguiente código que analiza una respuesta JSON de una URL, pero me estoy encontrando con un error con el objeto URL simulado:

    public String getJsonResponseFromUrl(String url) {
        JSONObject jsonObject = null;
        try {
            URL apiUrl = new URL(url);
            jsonObject = new JSONObject(IOUtils.toString(apiUrl, StandardCharsets.UTF_8));
        } catch (IOException e) {
            System.out.println(e);
        }
        return jsonObject.toString();
    }
    

    Solución

    Para simular el objeto URL, necesitas usar PowerMockito.whenNew() para interceptar la llamada al constructor de URL y devolver el objeto simulado en su lugar. Sin embargo, necesitas preparar la clase para simularla estáticamente, así como también la clase Service y la clase JSONObject. Esto se puede hacer con la anotación @PrepareForTest:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({URL.class, Service.class, JSONObject.class})
    public class ServiceTest {
    
        private JSONObject mockJsonObject;
        private URL mockUrl;
        private Service service;
    
        @Before
        public void setUp() throws Exception {
            mockJsonObject = mock(JSONObject.class);
            mockUrl = mock(URL.class);
            service = spy(new Service());
        }
    
        @Test
        public void testGetJsonResponseFromUrl() throws Exception {
            String url = "http://some-url.com";
            whenNew(URL.class).withArguments(url).thenReturn(mockUrl);
            whenNew(JSONObject.class).withArguments(anyString()).thenReturn(mockJsonObject);
    
            String result = service.getJsonResponseFromUrl(url);
    
            assertEquals(mockJsonObject.toString(), result);
        }
    }
    

    En el ejemplo anterior, usamos whenNew(URL.class) para crear una interceptación que devuelve el objeto simulado cuando se llama al constructor de URL con la URL especificada. Luego, usamos whenNew(JSONObject.class) para crear una interceptación que devuelve el objeto simulado cada vez que se llama al constructor de JSONObject con cualquier argumento de cadena. Finalmente, llamamos al método service.getJsonResponseFromUrl(url) y comprobamos que el resultado sea el mismo que el JSONObject simulado.

    Además, para activar el método simulado getJsonResponseFromUrl(), usamos service = spy(new Service()); ya que @InjectMocks no es necesario aquí.

    Espero que esta solución ayude a resolver tu problema.

Comments are closed.