Clases de pruebas unitarias que tienen funcionalidad en línea

23

Cuando la unidad prueba funciones de una clase que tiene funciones privadas que requieren funcionalidad en línea. ¿Cómo se podría probar?

Por ejemplo:

public class Foo
{
    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        CloudService c = new CloudService();
        int oval = c.getValueFromService();
        return oval;
    }
}

Si tuviera que probar la función: 'methodA ()', intentaría usar 'goOnlineToGetVal ()', que a su vez intentaría conectarse en línea, si esta prueba se realizara sin funcionalidad. ¿Cómo obtendría una cobertura de clase del 100% sin estar en línea?

Ben enloquecido Euden
fuente
¿Su método solo llama a la API de la nube, o también realiza un trabajo adicional?
Winston Ewert
44
Inyección de dependencia ?
PhaDaPhunk

Respuestas:

76

new CloudService()

Y ahí está tu problema.

El diseño moderno de OO recomienda que este tipo de dependencia se transfiera en lugar de construirse directamente. Esto se puede pasar a la función misma o a la clase en el momento de la construcción. También podría ser agarrado o agregado por un contenedor de Inversión de Control si se justifica ese tipo de complejidad.

En ese momento, se vuelve bastante trivial pasar un servicio falso / falso para proporcionarle sus datos "en línea" durante las pruebas. Mejor aún, permite que su software sea lo suficientemente flexible, para que pueda adaptarse rápidamente en caso de que algún cliente (¿gubernamental?) Aparezca y no quiera usar la nube para sus valores. O desea volcar un proveedor de la nube para otro. O...

Telastyn
fuente
77
Creo que me he acercado mucho más a comprender qué es la inyección de dependencia.
marczellm
¿Dónde está el mejor lugar para crear todas las instancias que necesita mi aplicación? ¿Tan cerca de la parte superior de mi aplicación como sea posible? Solo puede aplicar la inyección de dependencia hasta ahora; en algún momento necesitará crear las instancias usted mismo en lugar de que se pasen como argumentos.
Paul
3
@Paul - Depende. En mi experiencia, las aplicaciones a menudo se ven como árboles binarios: una clase / función / módulo pega dos juntas para proporcionar un comportamiento más complejo. Una de esas clases de "pegamento" es la que especifica la implementación concreta, que a su vez es utilizada por algo anterior que lo pega con otra cosa, que a su vez ... hasta que finalmente llega a su punto de entrada que pega las piezas grandes juntas coherentemente El "mejor" lugar es el lugar que le permite a su código hacer lo que necesita hacer, sin obligarlo a hacer o saber cosas que no debería hacer. Variará
Telastyn
2
@Paul Por lo general, su aplicación se divide entre una "biblioteca", que se prueba directamente y debe usar este tipo de inyección de dependencia y un código específico de la aplicación. Este último generalmente se reduce a alguna GUI, algún servicio web o alguna interfaz de línea de comandos que llama directamente a la biblioteca con datos proporcionados por el usuario. Esencialmente, el código específico de la aplicación creará las implementaciones concretas que la biblioteca inyectada por dependencia espera.
Darkhogg
@Paul Eche un vistazo a los contenedores IoC como Castle Windsor, StructureMap, Ninject y Unity. De ninguna manera son la única forma de hacer una inyección de dependencia, pero pueden ser muy informativos en términos de pensar en la construcción de un gráfico de objeto, de arriba hacia abajo
Ben Aaronson
37

Lo implementaría así:

public class Foo
{
    private ICloudService cloudService;

    public Foo(ICloudService s)
    {
       cloudService=s;
    }

    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        int oval = cloudService.getValueFromService();
        return oval;
    }
}

La interfaz ICloudServicese puede implementar con un simulacro de prueba o con el servicio en la nube "real". Cuando la instanciación new CloudService()es obligatoria para cada llamada de getValueFromService, o CloudServicees de una API de terceros que no se puede cambiar, implemente un contenedor, derivando ICloudServicey haciendo las llamadas apropiadas.

Doc Brown
fuente
1
Este es definitivamente el camino a seguir. Hay bibliotecas de clases que pueden simular una interfaz que le brinda un control absoluto de la prueba.
Greg Burghardt