mocking private static
Impara i metodi Mocking Private, Static e Void in Mockito con esempi:
In questa serie di hands-on Tutorial su Mockito , abbiamo esaminato il diversi tipi di Mockito Matchers nell'ultimo tutorial.
In generale, la derisione dei metodi privati e statici rientra nella categoria della beffa insolita.
Se sorge la necessità di deridere metodi / classi privati e statici, indica un codice con un refactoring inadeguato e non è realmente un codice testabile ed è molto probabile che un codice legacy che non è stato utilizzato sia molto amichevole per i test di unità.
Detto questo, esiste ancora il supporto per i metodi privati e statici Mocking da parte di pochi framework di unit test come PowerMockito (e non direttamente da Mockito).
I metodi di derisione 'void' sono comuni in quanto potrebbero esserci metodi che essenzialmente non restituiscono nulla, come l'aggiornamento di una riga del database (considerala come un'operazione PUT di un endpoint Rest API che accetta un input e non restituisce alcun output).
Mockito fornisce il supporto completo per deridere i metodi void, che vedremo con esempi in questo articolo.
come scrivere casi di test nei test manuali
Cosa imparerai:
- Powermock - Una breve introduzione
- Metodi privati beffardi
- Metodi statici beffardi
- Mocking Void Methods
- Suggerimenti e trucchi
- Conclusione
- Lettura consigliata
Powermock - Una breve introduzione
Per Mockito, non esiste un supporto diretto per simulare metodi privati e statici. Per testare metodi privati, sarà necessario refactoring del codice per modificare l'accesso a protected (o package) e dovrai evitare metodi statici / finali.
Mockito, a mio parere, intenzionalmente non fornisce supporto per questo tipo di mock, poiché l'uso di questi tipi di costrutti di codice sono odori di codice e codice mal progettato.
Tuttavia, ci sono framework che supportano il mocking per metodi privati e statici.
Powermock estende le capacità di altri framework come EasyMock e Mockito e fornisce la capacità di simulare metodi statici e privati.
# 1) Come: Powermock lo fa con l'aiuto della manipolazione del bytecode personalizzato al fine di supportare metodi privati e statici beffardi, classi finali, costruttori e così via.
# 2) Pacchetti supportati: Powermock fornisce 2 API di estensione: una per Mockito e una per easyMock. Per il bene di questo articolo, scriveremo esempi con l'estensione Mockito per power mock.
# 3) Sintassi :Powermockito ha una sintassi quasi simile a Mockito, ad eccezione di alcuni metodi aggiuntivi per deridere metodi statici e privati.
# 4) Configurazione Powermockito
Per includere la libreria Mockito in progetti basati su gradle, di seguito sono elencate le librerie da includere:
testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '1.7.4' testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.7.4'
Dipendenze simili sono disponibili anche per Maven.
Powermock-api-mockito2 - La libreria è richiesta per includere le estensioni Mockito per Powermockito.
Powermock-module-junit4 - Il modulo è necessario per includere PowerMockRunner (che è un runner personalizzato da utilizzare per l'esecuzione di test con PowerMockito).
Un punto importante da notare qui è che PowerMock non supporta il test runner Junit5. Quindi i test devono essere scritti su Junit4 e i test devono essere eseguiti con PowerMockRunner.
Per utilizzare PowerMockRunner, la classe di test deve essere annotata con @RunWith (PowerMockRunner.class)
Ora parliamo, deridendo in dettaglio i metodi privati, statici e vuoti!
Metodi privati beffardi
La derisione dei metodi privati, che vengono chiamati internamente da un metodo sottoposto a test, può essere inevitabile in determinati momenti. Utilizzando powermockito, questo è possibile e la verifica viene eseguita utilizzando un nuovo metodo denominato 'verifyPrivate'
Prendiamo unEsempio dove il metodo sottoposto a test chiama un metodo privato (che restituisce un valore booleano). Per eseguire lo stub di questo metodo in modo che restituisca true / false a seconda del test, è necessario impostare uno stub su questa classe.
Per questo esempio, la classe sottoposta a test viene creata come istanza spia con derisione su poche chiamate di interfaccia e invocazione di metodi privati.
Punti importanti per Mock Private Method:
# 1) Il metodo di test o la classe di test devono essere annotati con @ PrepareForTest (ClassUnderTest). Questa annotazione dice a powerMockito di preparare determinate classi per il test.
Queste saranno principalmente quelle classi che devono essere Bytecode manipolato . In genere per le classi finali, classi contenenti metodi privati e / o statici che devono essere presi in giro durante il test.
Esempio:
@PrepareForTest(PriceCalculator.class)
#Due) Per impostare lo stub su un metodo privato.
Sintassi - quando (istanza fittizia o spia, 'privateMethodName'). thenReturn (// valore restituito)
Esempio:
when (priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false);
# 3) Per verificare il metodo privato stubbed.
Sintassi - verifyPrivate (mockedInstance) .invoke ('privateMethodName')
Esempio:
verifyPrivate (priceCalculator).invoke('isCustomerAnonymous');
Campione di prova completo: Continuando lo stesso esempio degli articoli precedenti, dove priceCalculator ha alcune dipendenze fittizie come itemService, userService ecc.
Abbiamo creato un nuovo metodo chiamato - calculatePriceWithPrivateMethod, che chiama un metodo privato all'interno della stessa classe e restituisce se il cliente è anonimo o meno.
@Test @PrepareForTest(PriceCalculator.class) public void calculatePriceForAnonymous_witStubbedPrivateMethod_returnsCorrectPrice() throws Exception { // Arrange ItemSku item1 = new ItemSku(); item1.setApplicableDiscount(5.00); item1.setPrice(100.00); double expectedPrice = 90.00; // Setting up stubbed responses using mocks when(priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false); when(mockedItemService.getItemDetails(123)).thenReturn(item1); // Act double actualDiscountedPrice = priceCalculatorSpy.calculatePriceWithPrivateMethod(123); // Assert verifyPrivate(priceCalculator).invoke('isCustomerAnonymous'); assertEquals(expectedPrice, actualDiscountedPrice); }
Metodi statici beffardi
I metodi statici possono essere derisi in un modo simile a quello visto per i metodi privati.
Quando un metodo sottoposto a test prevede l'utilizzo di un metodo statico della stessa classe (o di una classe diversa), sarà necessario includere quella classe nell'annotazione prepareForTest prima del Test (o sulla classe di test).
Punti importanti per i metodi statici fittizi:
# 1) Il metodo di test o la classe di test devono essere annotati con @ PrepareForTest (ClassUnderTest). Simile alla derisione di metodi / classi privati, questo è richiesto anche per le classi statiche.
#Due) Un passaggio aggiuntivo richiesto per i metodi statici è: mockStatic (// nome della classe statica)
Esempio:
mockStatic(DiscountCategoryFinder.class)
# 3) Per impostare lo stub su un metodo statico, è buono come lo stub di qualsiasi metodo su qualsiasi altra istanza mock dell'interfaccia / classe.
Per esempio: Per stub getDiscountCategory () (che restituisce un'enumerazione DiscountCategory con valori PREMIUM e GENERAL) metodo statico della classe DiscountCategoryFinder, è sufficiente eseguire lo stub come segue:
when (DiscountCategoryFinder. getDiscountCategory ()).thenReturn(DiscountCategory. PREMIUM );
# 4) Per verificare la configurazione fittizia sul metodo final / static, è possibile utilizzare il metodo verifyStatic ().
Esempio:
verifyStatic (DiscountCategoryFinder.class, times (1));
Mocking Void Methods
Proviamo prima a capire che tipo di casi d'uso potrebbero comportare lo stubbing dei metodi void:
# 1) Il metodo chiama ad esempio, che invia una notifica e-mail durante il processo.
come rimuovere qualcosa da un array java
Per esempio :Supponiamo che tu modifichi la password per il tuo account di internet banking, una volta che la modifica è andata a buon fine riceverai una notifica tramite la tua email.
Questo può essere pensato come / changePassword come una chiamata POST all'API della banca che include una chiamata al metodo void per inviare una notifica e-mail al cliente.
#Due) Un altro esempio comune della chiamata al metodo void sono le richieste aggiornate a un DB che accetta un input e non restituisce nulla.
Lo stubbing dei metodi void (cioè i metodi che non restituiscono nulla, oppure lanciano un'eccezione), possono essere gestiti usando funzioni doNothing (), doThrow () e doAnswer (), doCallRealMethod () . Richiede che lo stub sia impostato utilizzando i metodi di cui sopra secondo le aspettative del test.
Inoltre, tieni presente che tutte le chiamate al metodo void sono per impostazione predefinita derise a doNothing (). Quindi, anche se non viene eseguita una configurazione fittizia esplicita VUOTO chiamate al metodo, il comportamento predefinito è ancora doNothing ().
Vediamo esempi per tutte queste funzioni:
Per tutti gli esempi, supponiamo che ci sia una classe StudentScoreUpdates che ha un metodo calcolaSumAndStore (). Questo metodo calcola la somma dei punteggi (come input) e chiama a vuoto metodo updateScores () sull'istanza databaseImplementation.
public class StudentScoreUpdates { public IDatabase databaseImpl; public StudentScoreUpdates(IDatabase databaseImpl) { this.databaseImpl = databaseImpl; } public void calculateSumAndStore(String studentId, int() scores) { int total = 0; for(int score : scores) { total = total + score; } // write total to DB databaseImpl.updateScores(studentId, total); } }
Scriveremo unit test per la chiamata al metodo mock con gli esempi seguenti:
# 1) non fare nulla () - doNothing () è il comportamento predefinito per le chiamate al metodo void in Mockito, cioè anche se verifichi una chiamata sul metodo void (senza impostare esplicitamente un void per doNothing (), la verifica avrà comunque successo)
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int() scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(anyString(), anyInt()); }
Altri usi insieme a doNothing ()
per) Quando il metodo void viene chiamato più volte e si desidera impostare risposte diverse per invocazioni diverse, come - doNothing () per la prima chiamata e lancia un'eccezione alla chiamata successiva.
Per esempio :Imposta mock in questo modo:
Mockito. doNothing ().doThrow(new RuntimeException()).when(mockDatabase).updateScores( anyString (), anyInt ());
b) Quando si desidera acquisire gli argomenti con cui è stato chiamato il metodo void, è necessario utilizzare la funzionalità ArgumentCaptor in Mockito. Ciò fornisce un'ulteriore verifica degli argomenti con cui è stato chiamato il metodo.
Esempio con ArgumentCaptor:
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int() scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); ArgumentCaptor studentIdArgument = ArgumentCaptor.forClass(String.class); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(studentIdArgument.capture(), anyInt()); assertEquals('Student1', studentIdArgument.getValue()); }
# 2) doThrow ()- Ciò è utile quando si desidera semplicemente generare un'eccezione quando il metodo void viene richiamato dal metodo sottoposto a test.
Per esempio:
Mockito.doThrow(newRuntimeException()).when(mockDatabase).updateScores ( anyString (), anyInt ());
# 3) doAnswer ()- doAnswer () fornisce semplicemente un'interfaccia per eseguire una logica personalizzata.
Per esempio. Modifica di un valore tramite gli argomenti passati, restituendo valori / dati personalizzati che un normale stub non avrebbe potuto restituire specialmente per i metodi void.
A scopo dimostrativo, ho bloccato il metodo void updateScores () per restituire un ' risposta() 'E stampa il valore di uno degli argomenti che avrebbero dovuto essere passati quando il metodo avrebbe dovuto essere chiamato.
Esempio di codice:
@Test public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabaseImpl); int() scores = {60,70,90}; Mockito.doCallRealMethod().when(mockDatabaseImpl).updateScores(anyString(), anyInt()); doAnswer(invocation -> { Object() args = invocation.getArguments(); Object mock = invocation.getMock(); System.out.println(args(0)); return mock; }).when(mockDatabaseImpl).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabaseImpl, Mockito.times(1)).updateScores(anyString(), anyInt()); }
# 4) doCallRealMethod ()- I mock parziali sono simili agli stub (dove puoi chiamare metodi reali per alcuni dei metodi e stub il resto).
Per i metodi void, mockito fornisce una funzione speciale chiamata doCallRealMethod () che può essere utilizzata quando si tenta di impostare il mock. Ciò che farà è chiamare il vero metodo void con gli argomenti effettivi.
Per esempio:
Mockito. doCallRealMethod ().when(mockDatabaseImpl).updateScores( anyString (), anyInt ());
Suggerimenti e trucchi
# 1) Includere più classi statiche nello stesso metodo / classe di test- Utilizzando PowerMockito se è necessario simulare più classi statiche o finali, i nomi delle classi in @ PrepareForTest l'annotazione può essere menzionata come valore separato da virgole come un array (essenzialmente accetta un array dei nomi delle classi).
Esempio:
@PrepareForTest({PriceCalculator.class, DiscountCategoryFinder.class})
Come mostrato nell'esempio sopra, supponi che PriceCalculator e DiscountCategoryFinder siano classi finali che devono essere prese in giro. Entrambi possono essere menzionati come un array di classi nell'annotazione PrepareForTest e possono essere stub nel metodo di test.
# 2) Posizionamento dell'attributo PrepareForTest - Il posizionamento di questo attributo è importante per quanto riguarda il tipo di test inclusi nella classe Test.
Se tutti i test devono utilizzare la stessa classe finale, allora ha senso menzionare questo attributo a livello di classe di test, il che significa semplicemente che la classe preparata sarà disponibile per tutti i metodi di test. Al contrario, se l'annotazione è menzionata nel metodo di prova, sarà disponibile solo per quel particolare test
Conclusione
In questo tutorial, abbiamo discusso vari approcci per simulare metodi statici, finali e void.
Anche se l'utilizzo di molti metodi statici o finali ostacola la testabilità e, tuttavia, è disponibile il supporto per test / mocking per assistere nella creazione di unit test al fine di ottenere una maggiore fiducia nel codice / applicazione anche per il codice legacy che generalmente non è utilizzato essere progettato per la testabilità.
Per i metodi statici e finali, Mockito non ha un supporto predefinito, ma librerie come PowerMockito (che ereditano pesantemente molte cose da Mockito) forniscono tale supporto e devono effettivamente eseguire la manipolazione del bytecode per supportare queste funzionalità.
Mockito out of the box supporta metodi di stubbing void e fornisce vari metodi come doNothing, doAnswer, doThrow, doCallRealMethod ecc. E può essere utilizzato secondo i requisiti del test.
Le domande di intervista a Mockito più frequenti sono descritte nel nostro prossimo tutorial.
Tutorial PREV | PROSSIMO Tutorial
Lettura consigliata
- Tutorial Mockito: Mockito Framework per Mocking in Unit Testing
- Le 12 migliori domande per l'intervista Mockito (Intervista sul quadro beffardo)
- Statico in C ++
- Thread Java con metodi e ciclo di vita
- Creazione di mock e spie in Mockito con esempi di codice
- Diversi tipi di matcher forniti da Mockito
- Metodi e tecniche di prevenzione dei difetti
- Come utilizzare i metodi in SoapUI per l'esecuzione di test in blocco - SoapUI Tutorial # 10