2011-01-26 3 views
9

Sto usando EasyMock per simulare oggetti nei miei test. Ma come faccio a imitare gli oggetti creati da qualche altra parte nel mio codice? Guarda il seguente codice di psudo. Voglio prendere in giro WebService # getPersonById, come faccio?Come faccio a simulare oggetti che non posso istanziare nei miei test?

public class Person { 
    public Person find(int id) { 
    WebService ws = new WebService(); 
    return ws.getPersonById(id); 
    } 
} 

public class PersonTest { 
    testFind() { 
    // How do I mock WebService#getPersonById here? 
    } 
} 

risposta

0

Ho finito per utilizzare invece JMockit. Ha il supporto per deridere tutte le istanze di una classe.

+2

Mi spiace dirlo, ma è un peccato. IMHO una delle grandi cose sullo sviluppo guidato dai test è che sei costretto a pensare e ripensare al tuo design. Trovare qualcosa che non è facilmente testabile contro le interfacce è un buon segno di odore di codice. @hvgotcodes ha tracciato un buon percorso, in che modo è possibile rendere il codice più gestibile, flessibile e verificabile. Non vorrei buttarlo via per una semplice soluzione. –

9

derisione in genere funziona bene se si utilizza l'inversione di controllo e di iniezione di dipendenza per cablare i propri servizi. Così la vostra persona dovrebbe assomigliare

public class Person() { 
    WebService ws = null; 

    // or use setters instead of constructor injection 
    Persion(WebService ws) { 
    this.ws = ws; 
    } 
    public Person find(int id) { 
    return ws.getPersonById(id); 
    } 
} 

si spera, è chiaro che con questo cambiamento, è ora possibile creare un controllo finto e finta per WebService e basta collegarlo nel test, perché quando si crea la persona alla prova , puoi passare la simulazione al costruttore (o setter se segui quella strada).

Nel vostro ambiente reale, il contenitore IoC inietterà il servizio web reale.

Ora, se non si vuole affrontare con tutta questa roba CIO, che cosa dovete fare è di disaccoppiare il tuo webservice da la tua Persona (che dovrebbe essere chiamata PersonService o qualcosa, non solo Persona, che denota entità). In altre parole, il modo in cui il codice viene scritto è possibile utilizzare solo un tipo di WebService. Hai bisogno di fare in modo che il persona ha solo bisogno di un po 'di tipo di WebService, non quella specifica avete hard-coded in.

Infine, nel codice come scritto, WebService è una classe, non un'interfaccia. Il WebService dovrebbe essere un'interfaccia e dovresti implementare una sorta di implementazione. EasyMock funziona bene con le interfacce; potrebbe essere in grado di prendere in giro classi concrete (è stato un po 'che non l'ho usato), ma come principio di progettazione è necessario specificare l'interfaccia richiesta, non la classe concreta.

+0

+1 - questo è quasi esattamente cosa ho aggiunto nella mia risposta (ora cancellata). –

+0

+1 Bella risposta. Modo di essere un insegnante. – jwir3

+1

Immagino si possa dire che c'è un odore di codice quando non si può prendere in giro un oggetto allora? :-) Credo di dover ripensare alla mia implementazione. Molte grazie per la tua fantastica risposta ed esempio. – Sven

0

Per prima cosa è necessario effettuare uno ws simulato, solitamente iniettandolo.

public abstract class Person() { 
    public Person find(int id) { 
    WebService ws = createWebService(); 
    return ws.getPersonById(id); 
    } 
    protected abstract WebService createWebService(); 
} 

Poi si può scivolare e utilizzare EasyMock.expect di impostare il ritorno

public class PersonTest() { 
    testFind() { 
    WebService mock = EasyMock.createMock(WebService.class); 
    Person p = new Persion() { 
     protected WebService createWebService() { 
     return mock; 
     } 
    } 
    EasyMock.expect(mock.getPersonById()).andReturn(dummyValue); 
    //Test code 
    } 
} 

Avrete anche bisogno di un PersonImpl di avere il vero creare metodo.

1

Penso che manchi un problema molto più grande. La difficoltà nel test sta cercando di dirti una cosa, che avere un oggetto Person (parte del dominio) che utilizza anche un servizio remoto per trovare ulteriori istanze di se stesso (parte del sistema) confonde le preoccupazioni. Separa l'acquisizione di Person s dall'oggetto Person e ti ritroverai con un codice più pulito e più portatile.

Non confondere la convenienza immediata (ho un oggetto Person nella mia mano, quindi lo userò per ottenere di più) con la manutenibilità.

3

Non c'è modo di farlo con EasyMock (o la maggior parte delle altre API di simulazione). Con JMockit, d'altra parte, un test del genere sarebbe molto semplice ed elegante:

public class PersonTest 
{ 
    @Test 
    public testFind(@Mocked final WebService ws) { 
     final int id = 123; 

     new NonStrictExpectations() {{ 
      ws.getPersonById(id); result = new Person(id); 
     }}; 

     Person personFound = new Person().find(id); 

     assertEquals(id, personFound.getId()); 
    } 
} 

Così, ogni volta ci imbattiamo in una situazione in cui una prova di unità non può essere scritto in un primo momento, non possiamo concludere automaticamente che il codice sotto test non è testabile e deve essere rifatto.A volte sarà il caso, ma certamente non sempre. Forse il problema non è nel codice in prova, ma nei limiti di un particolare strumento di simulazione che viene utilizzato.