Pregunta

Entonces, estaba leyendo el blog de pruebas de Google y dice que el estado global es malo y dificulta la redacción de pruebas.Lo creo: mi código es difícil de probar en este momento.Entonces, ¿cómo evito el estado global?

Lo más importante para lo que uso el estado global (según tengo entendido) es para gestionar 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 la aplicación se carga, determina qué conexión de conexión se cargar y poca 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.Entonces, cuando pruebo mis funciones, debo recordar establecer ciertos valores globales primero o, de lo contrario, las pruebas fallarán.Me gustaría evitar esto.

¿Existe una buena forma de gestionar la información estatal?(¿O estoy entendiendo incorrectamente el estado global?)

¿Fue útil?

Solución

La inyección de dependencia es lo que estás buscando.En lugar de hacer que esas funciones salgan y busquen sus dependencias, inyecte las dependencias en las funciones.Es decir, cuando llamas a las funciones, les pasas los datos que quieren.De esa manera, es fácil implementar un marco de prueba alrededor de una clase porque simplemente puedes inyectar objetos simulados cuando sea apropiado.

Es difícil evitar algún estado global, pero la mejor manera de hacerlo es usar clases de fábrica en el nivel más alto de su aplicación, y todo lo que esté por debajo de ese nivel superior 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 menos acoplada.Confías en poder programar en la interfaz de una clase en lugar de en su implementación.

Otros consejos

Tenga en cuenta que si sus pruebas involucran recursos reales, como bases de datos o sistemas de archivos, entonces lo que está haciendo es pruebas de integracion en vez de pruebas unitarias.Las pruebas de integración requieren cierta configuración preliminar, mientras que las pruebas unitarias deberían poder ejecutarse de forma independiente.

Podría considerar el uso de un marco de inyección de dependencia como Castle Windsor, pero para casos simples es posible que pueda adoptar un enfoque intermedio 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 más probable es que leas archivos de configuración en tu implementación.Si está dispuesto a hacerlo, el camino a seguir es un marco DI completo con configuraciones intercambiables, pero creo que esto es al menos mejor que usar Globals.ConnectionString.

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ás describiendo no parece un estado global.Al menos no un estado mutable.Más bien, lo que estás describiendo parece lo que a menudo se conoce como "El problema de configuración" y tiene varias soluciones.Si está utilizando Java, es posible que desee buscar marcos de inyección livianos como Guice.En Scala, esto generalmente se resuelve con implícitos.En algunos idiomas, podrá cargar otro programa para configurar su programa en tiempo de ejecución.Así es como solíamos configurar servidores escritos en Smalltalk, y yo uso un administrador de ventanas escrito en Haskell llamado Xmonad cuyo archivo de configuración es simplemente otro programa de Haskell.

Aquí va un ejemplo de inyección de dependencia en una configuración MVC:

index.php

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

contenedor.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 allí lo tiene, el controlador depende de un objeto de capa de servicio que depende de un objeto DAO (objeto de acceso a datos) que depende de un objeto de base de datos depende del controlador de la base de datos, el nombre, etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top