2008-09-04 12 views
8

Entonces, estaba leyendo el blog de pruebas de Google, y dice que el estado global es malo y hace que sea difícil escribir pruebas. Lo creo, mi código es difícil de probar en este momento. Entonces, ¿cómo evito el estado global?¿Cómo puedo evitar el estado global?

Las cosas más importantes que uso el estado global (como yo lo entiendo) es la gestión de piezas clave de información entre nuestros entornos de desarrollo, aceptación y producción. Por ejemplo, tengo una clase estática llamada "Globals" con un miembro estático llamado "DBConnectionString". Cuando se carga la aplicación, determina qué cadena de conexión cargar y completa Globals.DBConnectionString. Cargo rutas de archivos, nombres de servidores y otra información en la clase Globals.

Algunas de mis funciones dependen de las variables globales. Por lo tanto, cuando pruebo mis funciones, debo recordar establecer ciertas globales primero o de lo contrario las pruebas fallarán. Me gustaría evitar esto.

¿Hay una buena manera de administrar la información del estado? (¿O entiendo el estado global incorrectamente?)

Respuesta

10

Dependency injection es lo que estás buscando. En lugar de que esas funciones salgan y busquen sus dependencias, inyecte las dependencias en las funciones. Es decir, cuando llamas a las funciones, pasan los datos que ellos quieren. De esta forma, es fácil poner un marco de prueba alrededor de una clase porque simplemente puede inyectar objetos simulados donde corresponda.

Es difícil evitar un estado global, pero la mejor manera de hacerlo es utilizar clases de fábrica en el nivel más alto de su aplicación, y todo lo que está por debajo de ese nivel se basa en la inyección de dependencia.

Dos beneficios principales: uno, las pruebas son muchísimo más fáciles, y dos, su aplicación está mucho más débilmente acoplada. Confía en poder programar contra la interfaz de una clase en lugar de su implementación.

1

Gran primera pregunta.

La respuesta corta: asegúrese de que su aplicación sea una función desde TODAS sus entradas (incluidas las implícitas) hasta sus salidas.

El problema que está describiendo no parece ser un estado global. Al menos no estado mutable. Por el contrario, lo que está describiendo parece ser lo que a menudo se conoce como "El problema de configuración" y tiene una serie de soluciones. Si está utilizando Java, es posible que desee buscar marcos de inyección ligera como Guice. En Scala, esto generalmente se resuelve con implicits. En algunos idiomas, podrá cargar otro programa para configurar su programa en tiempo de ejecución. Así es como usamos para configurar los servidores escritos en Smalltalk, y utilizo un administrador de ventanas escrito en Haskell llamado Xmonad cuyo archivo de configuración es solo otro programa de Haskell.

2

que tener en cuenta si sus pruebas consisten en recursos reales, tales como bases de datos o sistemas de ficheros, entonces lo que está haciendo son pruebas de integración, más que pone a prueba la unidad . Las pruebas de integración requieren una configuración preliminar, mientras que las pruebas unitarias deben poder ejecutarse de forma independiente.

Se podría buscar en el uso de un marco de inyección de dependencias, como el castillo de Windsor, pero para los casos simples que usted puede ser capaz de tomar un medio del enfoque de la carretera, tales como:

public interface ISettingsProvider 
{ 
    string ConnectionString { get; } 
} 

public class TestSettings : ISettingsProvider 
{   
    public string ConnectionString { get { return "testdatabase"; } }; 
} 

public class DataStuff 
{ 
    private ISettingsProvider settings; 

    public DataStuff(ISettingsProvider settings) 
    { 
     this.settings = settings; 
    } 

    public void DoSomething() 
    { 
     // use settings.ConnectionString 
    } 
} 

En realidad lo que más lo haría probablemente lea desde archivos de configuración en su implementación.Si está preparado, un marco DI completo con configuraciones intercambiables es el camino a seguir, pero creo que al menos es mejor que usar Globals.ConnectionString.

0

Un ejemplo de inyección de dependencias en un entorno MVC, aquí va:

index.php

$container = new Container(); 
include_file('container.php'); 

container.php

container.add("database.driver", "mysql"); 
container.add("database.name","app"); 

...

$container.add(new Database($container->get('database.driver', "database.name")), 'database'); 
$container.add(new Dao($container->get('database')), 'dao'); 
$container.add(new Service($container->get('dao'))); 
$container.add(new Controller($container->get('service')), 'controller'); 

$container.add(new FrontController(),'frontController'); 

index.php continúa aquí:

$frontController = $container->get('frontController'); 
$controllerClass = $frontController->getController($_SERVER['request_uri']); 
$controllerAction = $frontController->getAction($_SERVER['request_uri']); 
$controller = $container->get('controller'); 
$controller->$action(); 

Y ahí lo tienes, el controlador depende de un objeto de capa de servicio que depende de un objeto DAO (acceso a objetos de datos) que depende de un objeto de base de datos con depende del controlador base de datos, nombre, etc.

Cuestiones relacionadas