¿Cómo puedo probar la unidad en una clase que requiere una llamada de servicio web?

21

Estoy tratando de probar una clase que llama a algunos servicios web de Hadoop. El código es más o menos de la forma:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

por ejemplo, hay un método de creación de directorio, un método de creación de carpeta, etc.

Dado que el código trata con un servicio web externo sobre el que no tengo control, ¿cómo puedo probar esto unitariamente? Podría intentar burlarme del cliente / respuestas del servicio web, pero eso rompe la pauta que he visto mucho recientemente: "No te burles de los objetos que no tienes". Podría configurar una implementación ficticia del servicio web, ¿constituiría eso una "prueba unitaria" o sería una prueba de integración? ¿No es posible realizar una prueba unitaria a este nivel tan bajo? ¿Cómo podría hacer esto un profesional de TDD?

Chris Cooper
fuente
55
¿Dónde has visto orientación sobre no burlarte de cosas que no te pertenecen? Esa parece ser una gran razón por la que deberías burlarte de las cosas ...
Thomas Owens
1
Lo he leído en muchos blogs, uno de los cuales hace referencia a amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/... que sé que es un libro bien considerado ( blog.8thlight. com / eric-smith / 2011/10/27 / thats-not-yours.html , mockobjects.com/2007/04/test-smell-everything-is-mocked.html )
Chris Cooper
1
@ChrisCooper: ¿Puedo señalar que el último enlace está muy desactualizado (desde 2007). Mucho ha cambiado desde entonces. La publicación me da la sensación de que burlarse fue mucho más difícil en aquel entonces cuando tenía que implementar el comportamiento en lugar de simplemente usar un marco de burla o metaprogramación para configurar los valores de retorno ...
c_maker

Respuestas:

41

En mi opinión, debe burlarse de las llamadas al servicio web si se trata de una prueba unitaria, en lugar de una prueba de integración.

La prueba de su unidad no debe probar si el servicio web externo está funcionando o si su integración con él es correcta. Sin entrar demasiado dogmática sobre TDD, nota que un efecto secundario de convertir su unidad de prueba en una prueba de integración es que es probable que se ejecute más lento, y desea rápidas pruebas unitarias.

Además, si el servicio web está temporalmente fuera de servicio o funciona incorrectamente, ¿esto debería causar que falle la prueba de la unidad? No parece correcto La prueba de su unidad debería fallar por una sola razón: si hay un error en el código de esa "unidad".

La única porción de código que es relevante aquí es ...do something with response.... Burlarse del resto.

Andres F.
fuente
2
Recuerde que tendrá que mantener su firma de objeto simulado y devolver los valores sincronizados con los generados por el servicio web de Hadoop durante los cambios inevitables en ese servicio.
pcurry
Raramente vale la pena probar componentes disponibles como Hadoop. Pero si está llamando a un servicio web personalizado proporcionado por otro equipo u organización, es posible que desee escribir una prueba en defensa propia. De esa manera, cuando las cosas salen mal, puede verificar rápidamente si el problema es su código o el servicio web. Esta no es una prueba unitaria para ejecutarse automáticamente; Es un diagnóstico para ejecutar según sea necesario.
Kevin Cline
@kevincline Estoy totalmente de acuerdo con la necesidad de las pruebas que propones, y de hecho las escribo en mi trabajo diario y he demostrado ser útiles. Pero, por definición, NO son pruebas unitarias, que es de lo que se trataba la pregunta :) Considere esto: si es una prueba unitaria y el código falla porque se modificó el servicio web, ¿cuál es la "unidad" que está probando? ¿Qué falló exactamente? No está realizando pruebas de forma aislada, como lo necesitan las pruebas unitarias.
Andres F.
1
@AndresF .: Creo que estamos en un acuerdo violento: "Este [diagnóstico] NO es una prueba unitaria ..."
Kevin Cline
@kevincline ¡Correcto! Leí mal tu comentario, lo siento.
Andres F.
5

No estoy de acuerdo con "no te burles de los objetos que no tienes" cuando estás probando unidades.

El propósito simulado de la existencia es el hecho de que habrá módulos, bibliotecas, clases que no poseeremos.

Mi sugerencia para su escenario es simular la llamada al servicio web.

Configure el simulacro de tal manera que devuelva los datos a su módulo.
Asegúrese de cubrir todos los escenarios, por ejemplo, cuando los datos devueltos sean nulos, cuando los datos devueltos sean válidos, etc.

Y para el código que posee, su responsabilidad como desarrollador es asegurarse de que el código que está creando funcione como se espera en todos los escenarios.

Venu b
fuente
1

Usaría algo como EasyMock para esta prueba. Los frameworks burlones son una forma ideal de eliminar dependencias externas de una clase y le brinda control total sobre el resultado de dependencias externas durante las pruebas. Para extender un poco su ejemplo:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

Lo primero que tendrá que hacer es extraer la lógica en su clase donde usa Jersey para obtener un recurso web y llamar al servicio web a una clase separada. Crear una interfaz para esta clase le permitirá crear una simulación a la que luego puede dictar el comportamiento.

Una vez que se crea esta interfaz, puede crear un simulacro con EasyMock, que devolverá un objeto específico según su caso de prueba. El ejemplo anterior es una simplificación de cómo estructurar una prueba simulada básica y cómo funcionará su interfaz.

Para obtener más información sobre los marcos de simulación, consulte esta pregunta . Además, este ejemplo supone el uso de Java, pero los marcos de simulación están disponibles en todos los idiomas y, aunque se implementan de manera diferente, generalmente funcionarán de la misma manera

Ricardo
fuente
1

Los simulacros son aceptables en este caso, pero no lo necesitas. En lugar de la prueba unitaria method(), prueba la unidad solo la parte que maneja la respuesta.

Extraiga una función que tome ResponseData(del tipo que sea apropiado) y luego realice la acción.

En lugar de burlarse, ahora solo construye un objeto ResponseData y lo pasa.

Puede dejar la llamada del servicio a pruebas de integración completas, que cubrirán method()en total

Daenyth
fuente
0

Lo que he hecho y funciona:

  1. Tener todos los servicios web de llamada de código a través de proxy.
  2. El proxy llama a una clase que sabe estáticamente si estamos usando proxy o no y redirige en consecuencia. Los simulacros son solo HashMaps que para cada solicitud devuelve una respuesta dada.
  3. Ejecute las pruebas varias veces en este orden:

3.1 Primero se prueban todos los servicios web. De cada máquina, incluso las máquinas del desarrollador. Estos son los servicios web reales, pero se ejecutan en entornos de desarrollo. Esto significa que los servicios web nunca pueden estar inactivos o responder a valores erróneos, porque de lo contrario cada desarrollador se queja de que no puede compilar.

3.2 Luego se ejecutan todas las pruebas unitarias internas de la aplicación. Esto significa que todos los servicios web se burlan y prueban ejecutando las mismas pruebas que 3.1 (y también deben pasar, de lo contrario, los simulacros son incorrectos), y la aplicación real los invoca como si realmente se estuvieran utilizando. Si los simulacros son incorrectos, puede ejecutar la prueba en 3.1 y registrar esos valores (solicitud, respuesta) en un HashMap.

3.3 Luego se ejecutan las mismas pruebas que 3.2, pero esta vez contra los servicios web reales que se ejecutan en el entorno de desarrollo.

Después de completar todo esto, para el entorno de producción real solo necesita proporcionar la dirección real para cada servicio web. Esperemos que esto no requiera demasiados cambios en la configuración.

Guillermo Schwarz
fuente