Pattern per l'interfaccia tra gli oggetti che eseguono la sequenza e gli oggetti di configurazione della sequenza

2

Il mio caso motivante è l'automazione del test hardware. Abbiamo diversi tester (banchi di apparecchiature da laboratorio che si collegano al "dispositivo in prova" (DUT)) e scriviamo i file di configurazione di test che istruiscono il tester su quali serie di test eseguire. I test eseguiti prendono anche i parametri dalla configurazione.

Al momento l'accoppiamento tra questi è più alto di quanto mi piacerebbe, quindi sto cercando di trovare un buon modello per mantenere la logica per come eseguiamo i test (oggetto "tester") separatamente da quali test vogliamo eseguire in ogni esecuzione particolare (gli oggetti di configurazione).

Il meglio che ho trovato finora è il seguente (sintassi non verificata, ma solo un'idea):

class TestStepConfig
{
    string Name;
    // Parameters are tuple of parameter name to parameter value. If parameter is not truly a string, it will be converted to int, float, etc later.
    IReadOnlyList<Tuple<string, string>> Parameters;
}

interface ITester
{
    // Return value is any data coming back from the test. First tuple element is result name, second is result value (I.E. "Voltage","5.0")
    List<Tuple<string, string>> RunTestStep(TestStepConfig config);
}

class NaiveSequencer
{

    List<TestStepConfig> configs;
    ITester tester;

    void RunFullTest()
    {
        foreach (var config in configs)
            tester.RunTestStep(config);
    }

}

L'idea è che ogni tester abbia una serie di passi di prova atomici che può eseguire. Conosce questi passaggi per nome e i passaggi contengono una serie di parametri come indicato nel campo Parametri di TestStepConfig. Quindi, si passa in un TestStepConfig con il nome del passo desiderato e dei parametri desiderati, e il tester esegue quella fase di test.

Quindi, tutto ciò che qualsiasi schema di configurazione deve fare è determinare quale TestStepConfigs inserire nel tester e tutto ciò che il tester deve capire è come eseguire questi TestSteps atomici.

Quello che mi ha guardato con questa soluzione è che "TestStepConfig" sembra a disagio come re-implementare una chiamata al metodo ("Nome" del metodo, "Parametri" da passare). Non voglio implementare i tester come un insieme effettivo di metodi specifici "test this, test that" perché:

  • Potrebbero esserci molte, molte di queste fasi di test disponibili (ovunque da 20 a oltre 100), che porta a oggetti estremamente grandi e rigidi.
  • Molti di questi test possono o non possono essere disponibili a seconda del particolare configurazione della panca (quale attrezzatura è disponibile, eccetera).
  • Come viene eseguito un TestStep di un nome particolare può cambiare
    a seconda della configurazione (quale parte viene testata, ecc.)
  • Devo essere in grado di eseguire sequenze di test con molte differenze tester, e non riesco a riscrivere tutti gli algoritmi di sequenziamento tempo c'è un nuovo tester solo perché ha un'interfaccia diversa

Per questi e altri motivi, i TestSteps che ritengo sono molto meglio modellati come un gruppo di qualcosa di simile a un Pattern di Comando (ma con parametri di esecuzione) riuniti da una fabbrica e inseriti in un'implementazione generica di un oggetto ITester , piuttosto che modellato come metodi in fase di compilazione del Tester.

Qualcuno ha avuto a che fare con un caso come questo prima? C'è uno schema migliore di quello che ho proposto sopra?

    
posta AHall1111 07.10.2015 - 19:32
fonte

1 risposta

2

Has anyone had to deal with a case like this before?

La tua configurazione banco / prova è più complessa di quella che ho affrontato, ma è lo stesso problema, solo più strati di costruzione.

Avviso: non lasciarti coinvolgere nell'inventare la struttura per generare e astrarre qualsiasi struttura, qualsiasi test, in qualsiasi momento.

Identifica, classifica e organizza ogni test

Se un test è un singolo metodo o una serie di metodi, o un composito di classe, sono tutti test e ogni test dovrebbe essere esplicitamente identificabile.

Ogni test dovrebbe essere progettato per funzionare da solo. Può essere eseguito come parte di una serie di test consecutivi, ma ogni test identificabile deve essere fondamentalmente indipendente.

Coding too soon

Pensare in termini di "metodi" è troppo ristretto, troppo subatomico. Ecco perché stai vedendo "l'accoppiamento tra questi è più alto di quanto vorrei". Ottieni l'organizzazione logica e il framework del codice emergerà da quello.

Osservazioni sulla struttura del test

  • TestBenches : crea una sezione di configurazione.
  • TestBench è probabilmente la parte superiore della struttura. Questo può anche fungere da spazio dei nomi, i test in diversi banchi di prova possono avere lo stesso nome.
  • La struttura si rifletterà direttamente negli elementi del file di configurazione.
  • L'ordine di prova nel file di configurazione è l'ordine di esecuzione.

Osservazioni di fabbrica

  • Avrai una fabbrica astratta. Potresti avere il modello di builder in base alle dinamiche di costruzione di ogni dato test.
  • La struttura astratta della fabbrica verrà mappata direttamente alla struttura del file di configurazione. Conosce la struttura. Sarà necessariamente strettamente accoppiato al file di configurazione.
  • I nomi degli elementi di configurazione verranno convertiti in enum s. I vari enum s rifletteranno la struttura di configurazione. Ad esempio TestBenchXFactory utilizzerà Xtests enum . Questo efficacemente convalida gli elementi XML prima che proviamo ad usarlo.

Osservazioni OO

  • Un sacco di soldi, ma è tutto un codice semplice fino a quando non arriviamo alla costruzione di oggetti di prova concreti e dinamici.
  • Il numero di fabbriche può sembrare eccessivo o del tutto inutile. Tuttavia, stiamo riducendo al minimo l'accoppiamento della costruzione di livelli
  • Aggiungendo, la rimozione dei test può sembrare una violazione dei principi Open / Closed, ma "sinceramente mio caro, non me ne frega niente."
  • La buona aderenza al principio di responsabilità unica nelle classi di fabbrica rende facile il cambiamento. Ancora, Ogni test logico è indipendente
<BenchTests>
    <!- BenchTestFactory gets <BenchTests> as a parameter -->
    <!- builds a BenchTestCollection. Instantiate sBenchTestXFactory, etc. -->
    <!- Adds concrete 'BenchTest's to the collection. -->
    <TestBenchX>
        <!- 'BenchTestXFactory' gets '<TestBenchX>' element as a parameter --> 
        <!- instantiates 'BenchTestX' object. Builds each test, adds to 'BenchTestX'. --> 
        <!- Certain tests may need their own builder -->
        <Test1 param1 = 2 param2 = "Hello".. />
        <Test2 />
        <ComplexTest ... >
            <Test1>
            <Test5>
        </ComplexTest>
    </TestBenchX>

    <TestBenchY>
    </TestBenchY>
</BenchTests>

Le classi

Da quanto sopra sembra che abbiamo:

public class testBenchCollection {
    List<TestBench> TestBenches;

    public void AddBench ( TestBench newBench ) {}

    public void Execute(){
        foreach( TestBench bench in testBenches )
            bench.Execute();
    }
}

public abstract class TestBench {
    List<Test> testCollection;

    public virtual Execute(){
        foreeach( Test aTest in testCollection )
            aTest.Execute();
    }

    public void AddTest( Test newTest )
}

public abstract class Test {
    public virtual Execute(params object[] myParms ){}
}

public enum TestBench{ X, Y, Z }
public enum GlobalTests { eenie, meenie, minee, moe }
public enum XTests { test1, testToo }
public enum YTests { Larry, Moe, CurleyJoe, test1 }
    
risposta data 18.10.2015 - 01:25
fonte

Leggi altre domande sui tag