Metodi statici di simulazione

3

Essendo recentemente tornato da un corso Test Driven Development (TDD) ho avuto il seguente pensiero.

Durante la scrittura dei test di unità usando Mockito ci siamo imbattuti nel problema di prendere in giro metodi statici. È stato suggerito da uno sviluppatore che usiamo PowerMock, che consente di prendere in giro metodi statici, e quindi abbiamo avuto una grande discussione su come dovremmo affrontare questo problema.

Tuttavia, secondo la mia esperienza, i metodi statici dovrebbero essere usati solo come metodi di tipo utility. L'esempio classico di java è Math.random ().

Quindi, solo un metodo statico dovrebbe eseguire un'azione definita a prescindere da qualsiasi interazione con un'istanza di classe. Se questo non è il tuo caso e hai bisogno di un'interazione di istanza di classe, il tuo metodo non dovrebbe essere statico ed è necessario rivedere il tuo design.

Quindi la mia domanda è, con questo in mente, dovresti aver bisogno di prendere in giro metodi statici? Se eseguono sempre un'azione semplice, sicuramente dovresti chiamarli come faresti nel codice reale.

    
posta T-Pane 07.03.2014 - 12:50
fonte

5 risposte

3

So my question is, with this in mind should you need to mock static methods at all? If they are always performing a simple action, then surely you should just call them as you would in real code.

Sì, se il codice è scritto correttamente. A volte non lo è, e hai ancora bisogno di testarlo. Il refactoring può essere molto dispendioso in termini di tempo (le statiche sono in genere associate ad un elevato accoppiamento e ad altri no-nos), quindi questa è una specie di soluzione alternativa.

Il refactoring potrebbe essere non solo irrealizzabile, ma impossibile. Il metodo statico potrebbe essere fuori dalla tua portata - cosa succede se il tuo codice deve chiamare un metodo statico da una libreria di terze parti, e non è stateless, richiede molto tempo per essere eseguito, ha requisiti speciali (database, accesso al web) ... Per testare il tuo codice devi prendere in giro questo metodo.

    
risposta data 07.03.2014 - 12:59
fonte
2

In generale, la risposta di @ KonradMorawski è buona e completa. Tuttavia, vorrei aggiungere che esiste uno scenario che considererei utile per simulare metodi static - il metodo static factory.

Personalmente, nel mio codice uso molto raramente metodi static factory; Li uso solo se l'oggetto creato è stateless, la factory è stateless e l'oggetto creato non ha dipendenze - e anche allora non sono ancora molto probabile la maggior parte delle volte. Altrimenti, utilizzo un'istanza di un oggetto factory, che può essere deriso.

Il problema con l'utilizzo di un metodo factory static come previsto è che gli oggetti creati non saranno mock s o spy s, il che significa che non potrai chiamare verify su di essi, e tu non controllerà in anticipo l'accesso a questi oggetti. Ad esempio, considera un'applicazione matematica simile alla seguente:

public abstract class ComplexNumber {
    private double realPart;
    private double imaginaryPart;
    ComplexNumber(double realPart, double imaginaryPart) {
        this.realPart = realPart;
        this.imaginaryPart = imaginaryPart;
    }
    public static ComplexNumber create(double realPart, double imaginaryPart) {
        return new ComplexNumber(realPart, imaginaryPart);
    }
}

public class RootCalculator {
    // Whatever is appropriate
    public List<ComplexNumber> roots(double... coefficients) {
        // somewhere in the code
        ComplexNumber number = new ComplexNumber(something, somethingElse);
        // blah blah
    }
}

public class RootCalculatorTest {
     @Test
     public testCalculator() {
         RootCalculator calc = new RootCalculator();
         List<ComplexNumber> roots = calc.roots(1, 0, 1); // equivalent to x^2 + 1 = 0
         for(ComplexNumber number : roots) {
             // verify(...);
         }
     }
}

Nota che non puoi chiamare verify sui numeri complessi restituiti, né puoi verify che il metodo factory sia stato chiamato il numero corretto di volte. Con PowerMock puoi fare entrambe le cose. Nota che l'uso di PowerMock qui è superiore alla sola chiamata new ComplexNumber() .

Ovviamente il modo migliore per farlo è iniettare un'istanza di una fabbrica, che può essere derisa, evitando PowerMock e permettendoti di fare tutto il normale comportamento di test.

    
risposta data 07.03.2014 - 14:00
fonte
0

I metodi statici falsi sono una cattiva idea, io uso solo se sto testando il codice legacy e non posso refactoring per qualsiasi motivo. Ma in un normale ciclo di sviluppo TDD è un cattivo odore.

Ricordo sempre una frase di Ian Cooper "prova le cose che vuoi conservare", quando stai prendendo in giro una statica che stai affermando nel nostro test che vuoi preservare non la funzionalità, vuoi preservare questo design con un chiamata al metodo statico. Sei sicuro di voler preservare questo?

    
risposta data 07.03.2014 - 15:03
fonte
0

Sono d'accordo con durron597 ma con un'eccezione. Ho scoperto questa domanda mentre stavo rivedendo parte del mio codice in cui avrei dovuto iniettare una fabbrica di simulazioni piuttosto che un oggetto fittizio poiché l'oggetto fittizio che stavo iniettando potrebbe dover cambiare all'interno del codice sotto test. Questo mi ha spinto ad analizzare un'altra sezione del codice in cui ho capito che ho un altro caso in cui mi sembra che dovrei usare un metodo statico di fabbrica. Come dice durron597 sopra:

"if the created object is stateless, the factory is stateless, and the created object has no dependencies"

Tuttavia, la parte con cui ho un problema è "l'oggetto creato non ha dipendenze". Non è questo il punto di prendere in giro un oggetto? Voglio rimuovere o sostituire le dipendenze e anche i valori che potrebbero essere restituiti da un'implementazione "reale".

Nel mio caso, voglio solo il mio metodo factory statico per restituire questo mock e sono a posto, proprio come lo ero quando era necessaria una sola istanza che stavo iniettando tramite un costruttore protetto. Quindi, mi rattrista pensare che dovrei usare PowerMock o abbandonare un uso perfettamente legittimo ed elegante di un costrutto linguistico per creare un'istanza di qualcosa che essenzialmente ha metodi che sono static nello spirito. E sì, come ho evitato prima, ho scoperto che stavo facendo esattamente quest'ultimo quando ho scoperto la nuova necessità di un'altra fabbrica altrove. Quindi, ho iniziato a modificare questo caso per utilizzare innanzitutto un metodo factory statico quando ho scoperto che Mockito non può simulare un static .

Inoltre, spaccare l'approccio "crea un'istanza" solo per fare i miei test unitari significa che ho un livello che ha conoscenza di qualcosa di cui non ha bisogno di sapere. Se la mia fabbrica è abbastanza semplice da non richiedere parametri o inizializzazione da questo o da un livello superiore, mi piacerebbe evitare l'accoppiamento richiesto da questo approccio e utilizzare il metodo statico di fabbrica solo al livello in cui è necessario. In questo caso, il livello di cui sopra non ha bisogno di sapere molto meno attenzione se c'è una fabbrica coinvolta o no!

Quindi, in sintesi, sì static CAN può essere un indicatore di odore ma, come con tutti i costrutti linguistici, IMHO c'è un tempo e un luogo adatti per tutti loro, ecco perché sono lì , destra? :)

    
risposta data 02.02.2015 - 21:15
fonte
0

Secondo la mia esperienza, puoi ragionevolmente prendere in giro metodi statici quando: * i metodi statici sono lenti e il suo utilizzo può ostacolare le prestazioni dei test unitari nel momento in cui gli sviluppatori smettono di eseguire i test. * il metodo statico proviene da una libreria di terze parti che non è possibile modificare e forse richiede dipendenze complesse che possono di nuovo rallentare l'esecuzione del test. * se sei veramente interessato all'interazione con il metodo statico più che nel risultato stesso.

Ad esempio, stai sviluppando una classe che funge da cache attorno a un metodo statico molto costoso. Quello che vuoi testare è probabilmente

  • che la prima volta che viene richiamata la cache, gli stessi parametri vengono forniti al costoso metodo statico.
  • che la seconda volta che la cache viene richiamata con un set di parametri già utilizzati, si ha un hit della cache e il metodo statico non viene chiamato affatto.

Questo è solo un esempio di base per farti capire la mia idea.

Devo ammettere che di solito cerco di non avere la necessità di simulare i metodi statici in test se sono in grado di farlo.

    
risposta data 11.01.2018 - 18:23
fonte

Leggi altre domande sui tag