2012-01-10 2 views
55
 
public class A { 

    public void method(boolean b){ 
      if (b == true) 
       method1(); 
      else 
       method2(); 
    } 

    private void method1() {} 
    private void method2() {} 
} 
 
public class TestA { 

    @Test 
    public void testMethod() { 
     A a = mock(A.class); 
     a.method(true); 
     //how to test like verify(a).method1(); 
    } 
} 

come testare metodo privato viene chiamato o no, e come testare metodo privato utilizzando Mockito ???Test Metodo privato utilizzando Mockito

risposta

48

Non è possibile farlo con Mockito ma è possibile utilizzare Powermock per estendere Mockito e simulare metodi privati. Powermock supporta Mockito. Here è un esempio.

+5

Sono confuso con questa risposta. Questo è beffardo, ma il titolo sta testando i metodi privati ​​ –

+0

Ho usato Powermock per deridere il metodo privato, ma come posso testare il metodo privato con Powermock. Dove posso passare qualche input e aspettarmi un po 'di output dal metodo e quindi verificare l'output? – Rito

1

Inserire il test nello stesso pacchetto, ma una cartella di origine diversa (src/main/java vs src/test/java) e rendere tali metodi package-private. La testabilità di Imo è più importante della privacy.

+5

L'unico motivo legittimo è quello di testare una parte di un sistema precedente.Se si inizia a testare il metodo private/package-private, si espongono le parti interne dell'oggetto. In questo modo, di solito si ottiene un codice scaricabile. Preferisci la composizione in modo da poter raggiungere la testabilità, con tutta la bontà di un sistema orientato agli oggetti. – Brice

+0

Concordato: sarebbe il modo preferito. Tuttavia, se vuoi veramente testare i metodi privati ​​con mockito, questa è l'unica opzione (tipicamente) che hai. La mia risposta è stata un po 'frettolosa però, avrei dovuto indicare i rischi, come te e gli altri hanno fatto. –

+0

Questo è il mio modo preferito. Non c'è niente di sbagliato nell'esporre l'oggetto interno a livello di pacchetto-privato; e unit test è un test white-box, è necessario conoscere l'interno per i test. –

91

Non possibile tramite mockito. Dal loro wiki

Perché Mockito non prende in giro metodi privati?

In primo luogo, non siamo dogmatici di prendere in giro metodi privati. A noi solo non interessa i metodi privati ​​perché dal punto di vista di non esistono metodi di test privati. Qui ci sono un paio di motivi Mockito non deridere i metodi privati:

Richiede l'hacking di classloader che non viene mai a prova di proiettile e cambia l'API (è necessario utilizzare il test corridore personalizzato, annotare la classe, etc.).

È molto facile da aggirare, basta cambiare la visibilità del metodo da privato a pacchetto protetto (o protetto).

Richiede che passi il tempo a implementare & mantenendolo. Ed è non ha senso dato il punto 2 e un fatto che è già implementato in strumento diverso (powermock).

Finalmente ... Il modo di deridere i metodi privati ​​è un suggerimento che c'è qualcosa di sbagliato nella comprensione di OO. In OO vuoi oggetti (o ruoli) a collaborare, non metodi. Dimentica il codice procedurale Pascal &. Pensa allo in oggetti.

4

Non si suppone di testare metodi privati. Solo i metodi non privati ​​devono essere testati in quanto dovrebbero comunque chiamare i metodi privati. Se si "desidera" testare metodi privati, potrebbe indicare che è necessario rivedere il proprio progetto:

Sto utilizzando un'iniezione di dipendenza appropriata? Devo forse spostare i metodi privati ​​in una classe separata e piuttosto testarla? Questi metodi devono essere privati? ... non possono essere predefiniti o protetti piuttosto?

Nell'istanza precedente, i due metodi che vengono chiamati "casualmente" possono effettivamente essere inseriti in una classe di loro proprietà, testati e quindi iniettati nella classe di cui sopra.

+13

Un punto valido. Tuttavia, non è forse questo il motivo dell'utilizzo di un modificatore privato per i metodi perché vuoi semplicemente abbattere codici troppo lunghi e/o ripetitivi? Separarlo come un'altra classe è come promuovere quelle linee di codici per essere cittadini di prima classe che non verranno riutilizzati da nessun'altra parte perché si trattava specificamente di partizionare codici lunghi e di impedire la ripetizione di righe di codice. Se lo separerai in un'altra classe, non sembra giusto; otterresti facilmente un'esplosione di classe. – supertonsky

+2

Notato il supertonsky, mi riferivo al caso generale. Sono d'accordo che nel caso di cui sopra non dovrebbe essere in una classe separata. (+1 sul tuo commento però - è un punto molto valido che stai facendo per promuovere i membri privati) –

+1

@supertonsky, non sono stato in grado di trovare alcuna risposta soddisfacente a questo problema. Ci sono diversi motivi per cui potrei usare membri privati ​​e molto spesso non indicano un odore di codice e trarrebbero grande vantaggio dal testarli. Le persone sembrano spazzare via questo dicendo "semplicemente non farlo". – LuddyPants

11

Pensa a questo in termini di comportamento, non in termini di quali metodi ci sono. Il metodo chiamato method ha un comportamento particolare se b è true. Ha un comportamento diverso se b è falso. Ciò significa che dovresti scrivere due test diversi per method; uno per ogni caso.Così, invece di avere tre prove di metodo-oriented (uno per method, uno per method1, una per method2, si dispone di due test comportamentali-oriented.

collegate a questo (ho suggerito questo in un altro SO filo di recente, e stato chiamato una parola di quattro lettere come risultato, quindi sentitevi liberi di prendere questo con un pizzico di sale), Trovo utile scegliere i nomi dei test che riflettono il comportamento che sto testando, piuttosto che il nome del metodo. chiamate i vostri test testMethod(), testMethod1(), testMethod2() e così via.Mi piace nomi come calculatedPriceIsBasePricePlusTax() o taxIsExcludedWhenExcludeIsTrue() che indicano quale comportamento sto testando, quindi all'interno di ciascun metodo di test, testare solo il comportamento indicato.Per la maggior parte di questi comportamenti verrà eseguita una sola chiamata a un metodo pubblico, ma può coinvolgere molti c tutto a metodi privati.

Spero che questo aiuti.

+3

@mlvljr Non si trattava tanto della denominazione, quanto della mia raccomandazione di scrivere un test per ogni comportamento, piuttosto che un test per ciascun metodo. Ma grazie per la risata! –

2

Non capisco davvero la necessità di testare il metodo privato. Il problema principale è che il tuo metodo pubblico è vuoto come tipo di reso e quindi non sei in grado di testare il tuo metodo pubblico. Quindi sei costretto a testare il tuo metodo privato. La mia ipotesi è corretta ??

alcune soluzioni possibili (AFAIK):

  1. beffardo vostri metodi privati, ma ancora non sarà "in realtà" testare i vostri metodi.

  2. Verificare lo stato dell'oggetto utilizzato nel metodo. I metodi MOSTLY eseguono l'elaborazione dei valori di input e restituiscono un output o cambiano lo stato degli oggetti. È anche possibile testare gli oggetti per lo stato desiderato.

    public class A{ 
    
    SomeClass classObj = null; 
    
    public void publicMethod(){ 
        privateMethod(); 
    } 
    
    private void privateMethod(){ 
        classObj = new SomeClass(); 
    } 
    
    } 
    

    [Qui è possibile verificare per il metodo privato, controllando il cambiamento di stato del classObj da null per non nullo.]

  3. refactoring del codice un po '(Spero che questo è non un codice legacy). La mia Funda di scrivere un metodo è che, si dovrebbe sempre restituire qualcosa (un int/un valore booleano). Il valore restituito può o non può essere usata dall'implementazione, ma sarà sicuramente utilizzato dal test di codice

    .

    public class A 
    { 
        public int method(boolean b) 
        { 
          int nReturn = 0; 
          if (b == true) 
           nReturn = method1(); 
          else 
           nReturn = method2(); 
        } 
    
        private int method1() {} 
    
        private int method2() {} 
    
    } 
    
15

Ecco un piccolo esempio di come farlo con powermock

public class Hello { 
    private Hello obj; 
    private Integer method1(Long id) { 
     return id + 10; 
    } 
} 

Per verificare method1 codice uso:

Hello testObj = new Hello(); 
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L)); 

Per impostare oggetto privato obj utilizzare questo:

Hello testObj = new Hello(); 
Hello newObject = new Hello(); 
Whitebox.setInternalState(testObj, "obj", newObject); 
+0

Penso che questa sia la risposta corretta. – Pranalee

+0

Il tuo collegamento punta solo al power re-mpo repo @Mindaugas – Xavier

+0

@Xavier true. Puoi usarlo se ti piace nel tuo progetto. – Mindaugas

3

Sono riuscito a testare un metodo privato all'interno utilizzando mockito utilizzando la riflessione. Ecco l'esempio, cercato di chiamarla tale che abbia senso

//Service containing the mock method is injected with mockObjects 

@InjectMocks 
private ServiceContainingPrivateMethod serviceContainingPrivateMethod; 

//Using reflection to change accessibility of the private method 

Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class}; 
    Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params); 
    //making private method accessible 
    m.setAccessible(true); 
    assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));