Sta prendendo in giro i test unitari in questo scenario?

8

Ho scritto circa 20 metodi in Java e tutti loro chiamano alcuni servizi web. Nessuno di questi servizi Web è ancora disponibile. Per continuare con la codifica lato server, ho hardcoded i risultati che il servizio web dovrebbe dare.

Possiamo unità testare questi metodi? Per quanto ne so, il test unitario sta prendendo in giro i valori di input e vede come risponde il programma. Stanno prendendo in giro sia i valori di input sia quelli di uscita significativi?

Modifica:

Le risposte qui suggeriscono che dovrei scrivere casi di test unitari.

Ora, come posso scriverlo senza modificare il codice esistente?

Considera il seguente codice di esempio (codice ipotetico):

    public int getAge()
    {
            Service s = locate("ageservice"); // line 1
            int age = s.execute(empId); // line 2
             return age; // line 3

 }

Ora Come prendiamo in giro l'output?

In questo momento, sto commentando "riga 1" e sostituendo riga 2 con int age= 50 . È giusto ? Qualcuno può indicarmi il modo giusto per farlo?

    
posta Vinoth Kumar C M 01.10.2012 - 14:19
fonte

7 risposte

12

Sì.

Usa mock e stub per simulare il comportamento previsto dei servizi web. Convalida che il tuo codice funzioni correttamente sotto tutti i valori limite previsti e le classi di equivalenza.

Modifica

No, non dovresti modificare manualmente il test dell'unità con int age= 50 . Dovresti utilizzare l'integrazione delle dipendenze in modo da poter facilmente sostituire Service con un oggetto fittizio.

public int getAge(Service s)
{
    int age = s.execute(empId); // line 2
    return age; // line 3
}

Il tuo test unitario dovrebbe assomigliare al seguente pseudo-codice:

public int getAgeMinimumValueTest()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.age = 10;
    int age = uut.getAge(service);
    Assert(age == 10);
}
    
risposta data 01.10.2012 - 14:43
fonte
3

Sì, dovresti prendere in giro il tuo servizio. Il mocking è la pratica dell'uso di oggetti mock per sostituire gli oggetti reali che normalmente useresti in fase di runtime. La maggior parte delle volte si vorrebbe usare una struttura di simulazione per creare oggetti mock in modo facile e dinamico.

Se usi gli oggetti mock, probabilmente scriverebbe un codice che assomiglia più a questo:

public int getAge(ServiceInterface service)
{
    return service.execute(empId);
}

dove service è il servizio effettivo che verrà utilizzato in fase di runtime, ma durante un test di unità è un oggetto fittizio con comportamento simulato. Si noti che il parametro per getAge non è più una classe concreta, ma un'interfaccia. Ciò consente di utilizzare qualsiasi oggetto come parametro purché implementa l'interfaccia, anziché una sola classe specifica. Il tuo test unitario seguirà quindi i seguenti passaggi:

  1. Crea l'oggetto mock
  2. Ditelo per restituire una certa età quando execute è chiamato
  3. Chiama getAge con l'oggetto mock come parametro
  4. Verifica getAge restituisce l'età corretta.

Per un buon elenco di framework di simulazione di Java, consulta questa domanda di stackoverflow .

Modifica

Non commentare il tuo codice e sostituiscilo con una costante solo per i tuoi test unitari. Il problema è che dovrai assicurarti che la cosa giusta venga commentata durante l'esecuzione dei test unitari e che la cosa giusta venga commentata al momento del rilascio al cliente. Ciò si trasformerebbe in un incubo per scopi di manutenzione a lungo termine, soprattutto se si intende scrivere più di un test di 2 unità che richiedono simulazioni. A parte questo, i test dovrebbero essere eseguiti contro il prodotto finito ; in caso contrario, ciò significa che non stai testando il codice effettivo che viene rilasciato, il che nega lo scopo di fare test in primo luogo.

    
risposta data 01.10.2012 - 15:34
fonte
1

Sì, lo è. Il tuo compito è dimostrare che il tuo codice fa la cosa giusta, dato il suo ambiente. I servizi web esterni fanno parte dell'ambiente; non è compito tuo testare il loro corretto funzionamento, è compito delle persone che scrivono i servizi. Il mapping di input / output che i test dovrebbero verificare è l'input / output per il tuo codice; se il codice produce input per un altro collaboratore per fare il suo lavoro, questo è strettamente incidentale.

In effetti, anche dopo che i servizi web sono disponibili, è probabilmente una buona idea mantenere i test delle unità utilizzando un ambiente simulato piuttosto che quello reale. Rende i test più semplici, meno fragili rispetto al contesto del sistema previsto e probabilmente anche più veloci.

    
risposta data 01.10.2012 - 14:47
fonte
1

Per aggiungere a la risposta eccellente di emddudley il più grande guadagno che puoi ottenere nel prendere in giro il servizio è essere in grado di prova cosa dovrebbe succedere che il servizio non funzioni correttamente. Lo pseudo-codice di test potrebbe essere simile a questo:

public int AgeMinimumValue_LogsServiceError_Test()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    try {
        int age = uut.getAge(service, logger);
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

E ora la tua implementazione è stata modificata con questo nuovo requisito

public int getAge(Service s, Logger l)
{
    try {
        int age = s.execute(empId);
        return age;
    }
    catch(Exception ex) {
        l.LogError(ex);
        throw;
    }
}

In altri scenari è più probabile che tu debba rispondere a risposte più complicate. Se il servizio prevede l'elaborazione della carta di credito, è necessario rispondere a Operazione riuscita, Servizio non disponibile, Carta di credito scaduta, Numero non valido, ecc. Danneggiando il servizio è possibile garantire che risponda a questi scenari in modo appropriato per la propria situazione. In questo caso devi prendere in giro l'input / output del servizio e il feedback che ottieni dal sapere che il codice che consuma funzionerà per tutti gli output noti è davvero significativo e prezioso.

EDIT: Ho appena notato che vuoi essere in grado di prendere in giro senza modificare il metodo esistente. Per fare ciò il metodo locate("ageservice"); dovrebbe essere cambiato per supportare gli oggetti mock nei test e localizzare il servizio reale una volta che è pronto. Questa è una variante del modello di localizzazione del servizio che astrae la logica per recuperare l'implementazione del servizio che stai utilizzando. Una versione veloce potrebbe essere simile a questa:

public Service locate(string serviceToLocate) {
    if(testEnvironment) // Test environment should be set externally
        return MockService(serviceToLocate);
    else
        return Service(serviceToLocate);
}

Tuttavia, la mia raccomandazione sarebbe spostare le dipendenze del servizio in Costruttori:

public int AgeMinimumValue_LogsServiceError_Test()
{
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    ClassUnderTest uut = new ClassUnderTest(service, logger);

    try {
        int age = uut.getAge();
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

Ora il metodo getAge non ha più la responsabilità di cercare il servizio in quanto è stato estratto dalla classe lasciando completamente un'implementazione simile a questa:

public int getAge()
{
    try {
        // _service is a private field set by the constructor
        int age = _service.execute(empId); 
        return age;
    }
    catch(Exception ex) {
         // _logger is a private field set by the constructor
        _logger.LogError(ex);
        throw;
    }
}
    
risposta data 01.10.2012 - 16:22
fonte
0

Are mocking both input and ouput values meaningful?

No, ma non stai prendendo in giro il valore di input. Il tuo test è in esecuzione e il servizio simulato verifica che venga acceduta la parte giusta del contratto. Che ti capiti di restituire un valore particolare è irrilevante.

Altrimenti, se il tuo metodo ha una qualche logica, allora il valore di ritorno è importante. Qui stai prendendo in giro gli input per la logica (il risultato del servizio web) e testando l'output.

    
risposta data 01.10.2012 - 15:24
fonte
0

Now, how can I write it without modifying the existing code ?

non puoi. Ma puoi creare un'interfaccia "IMyService" che puoi programmare contro quella che contiene tutti i metodi-firme dei servizi web.

public int getAge()
{
        IMyService s = getService(); 
        int age = s.execute(empId);
         return age; // line 3

}

In modalità Produzione getService(); restituirà un riferimento a un webservice funzionante completo e in modalità test un implemention alternativo (o mock) che restituisce i tuoi dati falsi.

    
risposta data 01.10.2012 - 15:54
fonte
0

Il mocking non riguarda gli input o gli output e sostituisce le dipendenze esterne. Quindi sì, è opportuno scrivere test unitari e prendere in giro i servizi web esterni.

Ora, per le cattive notizie: a seconda della lingua e degli strumenti disponibili, potresti non essere in grado di prendere in giro le dipendenze esterne a meno che il codice non sia stato progettato per permetterti di farlo. Se questo è il caso, scoprirai che i test si trasformano in piccoli test di integrazione piuttosto che semplici test di verifica.

Sul lato positivo sembra che tu sia autorizzato a modificare il codice (altrimenti come stai commentando) passare un'interfaccia al tuo servizio per permetterne il derisione (o usare qualche altra forma di iniezione di dipendenza )

Infine, ciò che si desidera testare sembra essere un puro involucro attorno al servizio esterno, non c'è quasi nulla da testare qui oltre a quello che si chiama il servizio. Poiché questa è fondamentalmente un'interfaccia, la maggior parte dei test dovrà essere eseguita successivamente a un livello più alto (test di integrazione)

    
risposta data 01.10.2012 - 15:36
fonte

Leggi altre domande sui tag