Impostazioni e test globali / dell'applicazione

1

Abbiamo un dizionario globale delle impostazioni dell'applicazione (letto da un DB all'avvio e aggiornato quando richiesto) e una classe statica con una serie di metodi per ottenere queste impostazioni. Questo è storico, ma non vedo alcuna ragione per cui sarebbe fatto diversamente oggi, a meno che non aggiunga valore nei test ...

Le opzioni per lo scambio di valori per testare diversi scenari credo includono:

  1. Aggiornamento dei valori del database prima del test
  2. Scherza i metodi statici e intercetta quelli che ti servono e consenti i valori predefiniti per quelli che non influiscono sul test
  3. Codice di refactory per prendere un'istanza di impostazioni (ad es. IDepartmentApiSettings) in fase di costruzione (iniezione del costruttore) e utilizzare questo invece dei metodi statici

I primi due funzionano senza modifica del codice, quindi sono preferiti sul codice legacy. Il terzo pertanto sarebbe utilizzabile solo per il nuovo codice (o codice che può essere facilmente refactored).

Ora ecco la mia domanda, c'è qualche vantaggio reale nell'iniettare le impostazioni in questo modo? Oppure vivresti con metodi statici e userai l'opzione 1 o 2 per mantenere la coerenza attraverso la base di codice?

Nota: il codice è C # ma non dovrebbe fare alcuna differenza, sarebbe la stessa domanda per Java, quindi non lo taggò.

    
posta user2000095-tim 30.03.2015 - 19:28
fonte

2 risposte

1

Personalmente, non mi piacerebbe vivere con metodi statici per mantenere la coerenza su una base di codice.

I vantaggi, che li consideri "reali" o meno, di iniettare le impostazioni mentono, come sempre nella libertà acquisita e gli svantaggi evitati.

Preferirei sempre iniettare un'istanza di impostazioni in quanto riduce il minimo e non forzare a utilizzare un framework di simulazione.

Prendere in giro i metodi statici è il migliore, suppongo. Dato che sei in C # e statico e virtuale non si mescolano lì, non riesco a pensare a un modo conveniente - come usare un discendente di test e sovrascrivere i metodi - per prendere in giro i metodi senza usare un framework di simulazione.

Aggiornamento dei valori del database prima che il test sia orribile. Poiché i test hanno una dipendenza esterna, diventano automaticamente test di integrazione. Può funzionare, ma entrambi i test devono utilizzare tutti gli stessi valori o aggiornare i valori ogni esecuzione del metodo di prova. Il primo è orribilmente restrittivo. Quest'ultimo rallenterà molto i tuoi test e farà sì che tutti li disabilitino ... In questo caso non è necessario utilizzarli.

In breve: preferisco l'iniezione, usa il mocking dei metodi statici come una stampella se non puoi permetterti di aggiornare tutti gli usi della tua classe di impostazioni statiche a un modello di iniezione tutto in una volta.

Se comprendo correttamente la tua configurazione con la classe delle impostazioni, un'altra opzione potrebbe essere quella di mantenere la tua classe statica, fare in modo che la lettura dei valori dal database avvenga come parte di una chiamata di inizializzazione su quella classe. Assegnare al metodo di inizializzazione due parametri: un nome / coppia di valori o qualsiasi altra cosa che è possibile passare dai test per compilare il dizionario delle impostazioni, l'altro per indicare se i valori devono essere letti dal database. Rendere i valori predefiniti come una lista vuota di coppie nome / valore e "si" leggere da db. (Potrebbe essere necessario utilizzare sovraccarichi in C # per consentire valori dei parametri predefiniti).

Ora hai le mani libere per i test senza influire sull'uso della produzione della classe statica.

  • Il codice di produzione chiamerebbe semplicemente Initialize senza utilizzare alcun parametro ad un certo punto all'avvio dell'applicazione.
  • Il codice di prova può chiamare con una lista vuota e "no" per cancellare le impostazioni.
  • Il codice di prova può chiamare con un elenco di coppie nome / valore e "no" per fornire un insieme limitato di valori specifici (eliminando ciò che c'era prima, se lo desideri).
  • Il codice di prova può chiamare con un elenco di coppie nome / valore e "sì" per fornire sostituzioni a qualsiasi cosa verrà letta dal database.
risposta data 17.04.2015 - 20:26
fonte
1

Quello che descrivi è un rivestimento sottilmente velato su Singleton . In sostanza, si dispone di uno stato globale e si sta tentando di canalizzare l'accesso attraverso quella classe. Il fatto che l'interfaccia sia statica invece che di proprietà dell'oggetto è il "rivestimento" a cui mi riferivo.

Questo è uno dei pochi usi accettabili per Singleton. Anche se quel modello è spesso applicato in modo scorretto, fornire un singolo punto di accesso a ciò che è una singola configurazione è un buon uso.

Quello che vorrei fare è andare oltre e svincolare l'implementazione di "ottenere queste impostazioni globali" da "chiamando questo metodo statico". Questa è la tua opzione tre .

Esempio di codice:

public class Settings {
  private static final Settings instance;
  static {
    // Insert code to create a Settings and populate instance dynamically here.
    // By default create a Settings, but allow tests to override with a subclass.
  }

  // Example of how to delegate:
  public static int getMaxWidgets() {
    return instance.getMaxWidgetsImpl();
  }

  protected int getMaxWidgetsImpl() {
    // Call the DB here, but allow subclasses to override.
    return ...;
  }
}

Ciò che fa è consentire di scambiare (iniettare) una diversa implementazione del codice che cerca le impostazioni usando un qualche meccanismo. Forse chiama un metodo per cambiarlo. In Java, potrei usare una proprietà di sistema con il nome della classe. Il punto è che è molto facile cambiare l'implementazione e nessuno del codice dell'applicazione è più saggio.

    
risposta data 17.04.2015 - 20:36
fonte

Leggi altre domande sui tag