È giusto saltare i test delle unità e procedere direttamente ai test di integrazione della scrittura se non è possibile testare l'unità in isolamento?

5

Secondo l'articolo di Martin Fowler,

link

È consigliabile scrivere più test unitari rispetto ai test di integrazione. Ciò significa idealmente che ogni unità di lavoro deve essere scritta con test unitari inclusi tutti i casi limite? Che dire se non sembra esserci alcun punto nel test delle unità, ad esempio, un modulo di autenticazione che dipende strongmente da ciò che il database restituirà e la cui unica operazione è quella di memorizzare l'utente di autenticazione nella sua istanza singleton?

È giusto saltare i test delle unità e procedere direttamente ai test di integrazione della scrittura?

C'è solo una ragione per cui posso pensare perché il test dell'unità deve ancora essere scritto per il modulo di autenticazione e cioè introdurre un livello di astrazione tra il livello del dominio e il livello di persistenza, in modo che l'autenticazione non sia strettamente accoppiata al l'ORM scelto e l'ORM di commutazione saranno più semplici.

    
posta Xegara 03.05.2017 - 07:23
fonte

3 risposte

9

Secondo me i test di integrazione sono i test più importanti, perché sono quelli che ti dicono se l'applicazione in realtà funziona quando è tutto messo insieme.

Tuttavia, hanno alcuni aspetti negativi

  • richiedono un sistema in esecuzione con tutte le dipendenze da testare contro
  • sono lenti da eseguire
  • alcuni test sono necessariamente distruttivi e quindi non possono essere eseguiti contro ambienti live.
  • tendono ad essere test di "livello superiore". ti diranno che c'è qualcosa che non va, ma non quello che è.

I test unitari non hanno questi aspetti negativi, puoi eseguire rapidamente centinaia di unit test su una macchina di sviluppo senza distribuire l'intera app.

Quindi non saltare i test unitari solo perché hai un test di integrazione e non saltare i test di integrazione perché hai dei test unitari.

Un trucco che puoi fare per salvare un po 'di codice è far sì che i tuoi test funzionino sia come unità sia come integrazione, consentendo loro di prendere in giro o di non prendere in giro le dipendenze a seconda di un input o di un'impostazione.

Ciò consente di eseguire rapidamente i test come test di unità per verificare il codice. Quindi come integrazione per verificare l'installazione e la distribuzione.

Modifica: esempio di come combinare integrazione e test unitari senza duplicazione del codice.

public class AuthServiceTests
{
    [TestCase("api.myapp.com/auth", "databaseuser", true, Category="Integration")]
    [TestCase("mock", "mockuser", true, Category="Unit")]
    public void IsUserTest(string target, string username, bool expected)
    {
        //SetUp
        if(target != "mock")
        {
            httpClient = new HttpClient(target);
        }
        else
        {
            httpClient = new MockHttpClient();
        }
        var authService = new AuthService(httpClient);

        var actual = authService.IsUser(user); 
//obvs this might fail if the databases changes in the integration version
//or the db isnt setup right, or the connection string in the api is wrong etc

//the mock one will only fail if the code has a bug of some kind, say it doesnt work with long usernames, or emoticons or something

        Assert.AreEqual(expected, actual);
    }
}
    
risposta data 03.05.2017 - 07:47
fonte
5

Nel mondo reale non si arriva quasi mai al 100% di copertura del test e tutti i casi d'angolo sono compresi. Quindi provi a coprire il più possibile con i test di unità e di integrazione.

Il fatto che non vedi alcun punto nello scrivere un test unitario perché un modulo dipende da una fonte di terze parti, è perché il tuo design probabilmente non consente il mocking.

Se hai un modulo che ha una dipendenza esterna (ad esempio file system o database), dovresti essere in grado di emulare quella dipendenza esterna e renderla restituita come preferisci. Quindi, avrai un test unitario perfettamente valido, che non si baserà sull'effettiva presenza del file o del record o altro, e potrà essere eseguito più volte.

Questo di solito ha un costo di progettazione ridotto e migliora la testabilità e la modularizzazione un po '.

Ad esempio, nel tuo caso Authenticator, il tuo codice dovrebbe ricevere un oggetto Connection / Database e interrogarlo, così puoi passarlo al tuo database di simulazione e lasciare che restituisca dati validi / non validi a volontà.

    
risposta data 03.05.2017 - 09:11
fonte
2

How about if there seems no point in unit testing, for instance, an authentication module which is heavily dependent on what the database will return and whose only operation is to store the authentication user to its singleton instance?

Questa è una falsa argomentazione. Se l'unico scopo dell'unità è quello di memorizzare e non convalidare, il test unitario dovrebbe testare proprio questo scopo. Convalidare ciò che il DB restituisce è fuori ambito per questo test e dovrebbe essere testato altrove. Potrebbe mancare un "validatore di credenziali".

Parte del test può essere tuttavia che l'unità utilizza il validatore di credenziali prima di considerare se stesso fatto.

La risposta formale sarebbe che qualsiasi funzione che non offre nulla da testare non ha scopo e non dovrebbe esistere.

    
risposta data 03.05.2017 - 08:58
fonte