2013-11-01 14 views
6

È vero che mockito non è in grado di simulare oggetti che sono già stati migliorati da CGLIB?Mocking Oggetti potenziati CGLIB

public class Article { 

    @Autowired 
    private dbRequestHandler 

    @Autowired 
    private filesystemRequestHandler 

    @Transactional 
    public ArticleDTO getArticleContents() { 

     //extractText() and then save the data in DTO 
     //extractImages() and then save the data in DTO 
     // some other calls to other databases to save data in dto 

     return articleDTO; 

    } 
    public void extractText() { 

     //call to DB 

    } 

    public void extractImages() { 

     // call to file system 

    } 
} 


public class IntegrationTest { 

    @Autowired 
    private Article article; 

    //setup method { 

    articleMock = Mockito.spy(article); 

    doNothing().when(articleMock).extractImages(); 
} 
} 

Nell'esempio precedente quando si tratta di doNothing().when(articleMock).extractImages(); chiama effettivamente la funzione reale. A ben vedere, l'articoloMock viene migliorato due volte. Una causa di autowiring e la seconda causa di tempo di spying.

Se non riesco a spiare oggetti enhaced, come posso testare il metodo getArticle() nel mio test di integrazione, in modo da poter verificare che venga restituito un DTO corretto.

Nota: in realtà non voglio testare il metodo che chiama il filesystem. solo quelli del DB. ecco perché ho bisogno di testare il metodo getArticle.

+0

In seguito a ciò che ho trovato di [documentazione] (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#doNothing()) non vedo immediatamente il problema. L'hai provato creando "l'articolo" da solo, e non farlo autowired (o almeno verificato la correttezza dopo l'autowiring)? – atomman

+0

sì, se creo l'articolo io stesso, sono in grado di spiare. Ma devo autorizzarlo, poiché nella mia applicazione ogni oggetto è creato da autowiring, e se avvio 'Article' da solo allora il campo in Article class è nullo (es. Gli oggetti reqHandler). Se avvii anche quei campi, i campi in quelle classi sono nulli e la catena continua. – samach

+0

Il codice filtrato e le tue domande non si adattano - 'getArticle()' in questione, 'getArticleContents()' nel codice - sta causando una certa confusione. Dovresti considerare di fornire un altro codice 'IntegrationTest'. – Cebence

risposta

3

Se ho capito bene, la tua classe è cablata da Spring. Spring utilizza CGLIB per garantire il comportamento transazionale solo se non esiste un'interfaccia, implementata dal proprio oggetto. Se esiste un'interfaccia, utilizza semplici proxy dinamici JDK. (vedi http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html)

Forse potresti provare a estrarre un'interfaccia e lasciare che Spring usi i proxy dinamici. Forse allora Mockito potrebbe esibirsi meglio.

1

Se si esegue un test di unità vero e non come un test di integrazione, non è necessario eseguire in un contenitore che dispone di Spring autowire per voi. In uno dei tuoi commenti, penso che hai alluso a provare questo, e hai notato che c'era un insieme infinito di riferimenti a oggetti concatenati che dovresti fornire anche tu. Ma c'è un modo per aggirare questo. Mockito fornisce alcune classi predefinite Answer con cui puoi inizializzare la tua simulazione. Potresti voler dare un'occhiata a RETURNS_DEEP_STUBS, che ti aiuterà a risolvere questo problema.

1

Si prega di aggiornare la domanda con il codice compilabile pronto per l'uso. Ecco alcuni suggerimenti revisione del codice:

Problemi con questo Codice domanda:

  • Article.java manca di importazione: org.springframework.beans.factory.annotation.Autowired
  • Article.java mancante di importazione: org.springframework.transaction.annotation.Transactional
  • Article.java problema sintassi attributo: dbRequestHandler
  • Article.java problema sintassi attributo: filesystemRequestHandler
  • metodo Article.java non ha alcuna istruzione return inizializzato: articleDTO

Ecco cosa si dovrebbe forse usare come si questionCode con le questioni di cui sopra fisse:

Article.java

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.transaction.annotation.Transactional; 

public class Article { 

    @Autowired 
    private Object dbRequestHandler; 

    @Autowired 
    private Object filesystemRequestHandler; 

    @Transactional 
    public ArticleDTO getArticleContents() { 

     // extractText() and then save the data in DTO 
     // extractImages() and then save the data in DTO 
     // some other calls to other databases to save data in dto 

     ArticleDTO articleDTO = null; 
     return articleDTO; 

    } 

    public void extractText() { 

     // call to DB 

    } 

    public void extractImages() { 

     // call to file system 

    } 
} 

IntegrationTest.java è un nome non valido per un testClass perché è generico. Suggerirei Articolo Test per un test di unità java.

ArticleTest.java

import org.junit.Assert; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.InjectMocks; 
import org.mockito.Mock; 
import org.mockito.Mockito; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 
import org.springframework.beans.factory.annotation.Autowired; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest(ClassWithPrivate.class) 
public class ArticleTest { 

    @InjectMocks 
    private Article cut; 

    @Mock 
    private Object dbRequestHandler; 

    @Mock 
    private Object filesystemRequestHandler; 

    @Test 
    public void testeExtractImages() { 

     /* Initialization */ 
     Article articleMock = Mockito.spy(cut); 

     /* Mock Setup */ 
     Mockito.doNothing().when(articleMock).extractImages(); 

     /* Test Method */ 
     ArticleDTO result = cut.getArticleContents(); 

     /* Asserts */ 
     Assert.assertNull(result); 

    } 

} 
0

Si può utilizzare AdditionalAnswers.delegatesTo metodo. Nell'esempio seguente, la dichiarazione secondProxyDoingMocking crea qualcosa come una spia (confrontare con l'implementazione del metodo spy() eccetto che utilizza la delega del metodo "leggera".

import org.mockito.AdditionalAnswers; 

public class ArticleTest { 

    @Autowired 
    private Article firstProxyDoingAutowiring; 

    @Test 
    public void testExtractImages() { 
     Article secondProxyDoingMocking = Mockito.mock(Article.class, 
       Mockito.withSettings().defaultAnswer(
         AdditionalAnswers.delegatesTo(firstProxyDoingAutowiring) 
       ) 
     ); 
     Mockito.doNothing().when(secondProxyDoingMocking).extractImages(); 
     ... 
    } 

} 

Non ho testato questo esempio, tuttavia l'ho montato dal mio codice di lavoro. Il mio caso d'uso era simile: restituire un valore costante per un dato metodo, chiamare il metodo reale per tutti i restanti metodi di Spring @Transactional -notificato bean.