Domanda

Quindi, stavo leggendo il blog sui test di Google, e dice che lo stato globale è negativo e rende difficile scrivere test.Ci credo: il mio codice è difficile da testare in questo momento.Allora come posso evitare lo stato globale?

La cosa più importante per cui utilizzo lo stato globale (per come lo intendo io) è la gestione di informazioni chiave tra i nostri ambienti di sviluppo, accettazione e produzione.Ad esempio, ho una classe statica chiamata "Globals" con un membro statico chiamato "DBConnectionstring". Quando l'applicazione si carica, determina quale stringa di connessione carichi e popola Globals.DBConnectionstring.Carico percorsi di file, nomi di server e altre informazioni nella classe Globals.

Alcune delle mie funzioni si basano sulle variabili globali.Quindi, quando provo le mie funzioni, devo ricordarmi di impostare prima determinati valori globali, altrimenti i test falliranno.Vorrei evitare questo.

Esiste un buon modo per gestire le informazioni sullo stato?(Oppure sto interpretando male lo stato globale?)

È stato utile?

Soluzione

L'iniezione di dipendenza è ciò che stai cercando.Piuttosto che far uscire quelle funzioni e cercare le loro dipendenze, inserire le dipendenze nelle funzioni.Cioè, quando chiami le funzioni, passa loro i dati che vogliono.In questo modo è facile mettere un framework di test attorno a una classe perché puoi semplicemente inserire oggetti mock dove appropriato.

È difficile evitare uno stato globale, ma il modo migliore per farlo è utilizzare le classi factory al livello più alto dell'applicazione e tutto ciò che è al di sotto di quel livello più alto si basa sull'inserimento delle dipendenze.

Due vantaggi principali:uno, il test è molto più semplice e, due, la tua applicazione è molto più flessibile.Ti affidi alla possibilità di programmare rispetto all'interfaccia di una classe piuttosto che alla sua implementazione.

Altri suggerimenti

Tieni presente che se i tuoi test coinvolgono risorse reali come database o filesystem, allora quello che stai facendo lo è test di integrazione piuttosto che test unitari.I test di integrazione richiedono alcune impostazioni preliminari mentre i test unitari dovrebbero essere in grado di essere eseguiti in modo indipendente.

Potresti esaminare l'uso di un framework di iniezione delle dipendenze come Castle Windsor, ma per i casi semplici potresti essere in grado di adottare un approccio a metà strada come:

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
    }
}

In realtà molto probabilmente leggeresti dai file di configurazione nella tua implementazione.Se sei pronto, un framework DI completo con configurazioni scambiabili è la strada da percorrere, ma penso che sia almeno migliore rispetto all'utilizzo di Globals.ConnectionString.

Ottima prima domanda.

La risposta breve:assicurati che la tua applicazione sia una funzione da TUTTI i suoi input (compresi quelli impliciti) ai suoi output.

Il problema che stai descrivendo non sembra uno stato globale.Almeno non stato mutabile.Piuttosto, quello che stai descrivendo sembra quello che viene spesso definito "Il problema di configurazione" e ha una serie di soluzioni.Se utilizzi Java, potresti voler esaminare framework di iniezione leggeri come Guice.In Scala, questo di solito viene risolto con impliciti.In alcune lingue, sarai in grado di caricare un altro programma per configurare il tuo programma in fase di runtime.Questo è il modo in cui configuravamo i server scritti in Smalltalk e io utilizzo un window manager scritto in Haskell chiamato Xmonad il cui file di configurazione è solo un altro programma Haskell.

Ecco un esempio di inserimento delle dipendenze in un'impostazione MVC:

indice.php

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

contenitore.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 continua qui:

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

E il gioco è fatto, il controller dipende da un oggetto a livello di servizio che dipende da un oggetto DAO (Oggetto Accesso Data) che dipende da un oggetto di database con dipende dal driver del database, ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top