Domanda per principianti: tutti i metodi pubblici di classe dovrebbero provenire da un'interfaccia?

4

Attualmente sto imparando le tecniche TDD, uno dei suggerimenti è testare solo metodi pubblici e saltare quelli privati. Ho anche letto su Mocking. Se voglio prendere in giro un certo metodo, allora deve provenire da un'interfaccia o essere contrassegnato come virtuale. Quando inizio a sviluppare la mia applicazione non so quali metodi mi piacerebbe prendere in giro durante la creazione dei test unitari, per questo penso che sia meglio renderli tutti disponibili per il mocking.

Suppongo che rendere tutti i metodi virtuali non sia la soluzione migliore, quindi l'alternativa è l'approccio di interfaccia menzionato sopra.

È la giusta direzione o mi manca qualcosa di ovvio?

    
posta ArturoO 08.08.2018 - 07:48
fonte

5 risposte

5
  • Fabio l'ha toccato nel suo commento - è perfettamente legittimo iniziare ad usare una dipendenza in un test prima che esista un'interfaccia o un codice di implementazione per esso.

    Quando ritieni che il tuo oggetto in prova abbia bisogno di input indiretto da qualche altra parte, o che devi testare output indiretti del tuo oggetto, inizia a digitare il tuo codice di stubbing o mocking come se esistessero tali dipendenze esterne. Quindi utilizza le funzionalità di produttività IDE per generare le interfacce e le relative firme dei metodi.

    Questo è chiamato " programmazione tramite wishful thinking " e viene usato molto in TDD esterno in .

    La tua domanda ha un senso in questo contesto, dal momento che non crei alcuna nuova interfaccia o alcun metodo all'interno di un'interfaccia esistente senza prima deriderli.

  • Un altro contesto è quando crei dipendenze estraendo nuove classi durante la fase Refactor del ciclo TDD e, in uno stile più "Classico", decidi di non testarle con i mock.

    Qui, la risposta alla tua domanda Should all class public methods come from an interface? è chiaramente: "no". È possibile avere perfettamente un test scritto su TDD che verifica gli oggetti A e B senza che nessuno di essi implementa un'interfaccia.

    Tuttavia, utilizzerei questo approccio solo quando la dipendenza estratta si trova nello stesso livello dell'oggetto sottoposto a test, non a confini / cuciture di applicazione . Avere interfacce ai confini architettonici è spesso più prezioso perché solitamente si ottiene la massima fragilità, la minima prestazione, la configurazione più complessa e la più alta probabilità di richiedere più implementazioni della stessa astrazione.

risposta data 08.08.2018 - 14:13
fonte
2

Should all class public methods come from an interface?

No: è un vincolo davvero interessante da sperimentare, ma non è necessario.

If I want to mock a certain method, then it needs to come from an interface or be marked as virtual.

Prendere in giro un metodo specifico mi rende nervoso - se stai provando a prendere in giro il metodo, piuttosto che l'oggetto, inizia a sembrare che tu non abbia realmente la grana corretta per i tuoi oggetti.

Se mi fossi imbattuto in questa esigenza, guarderei con molta attenzione per vedere se ho davvero due oggetti - uno che è responsabile del bit che voglio prendere in giro (e nient'altro), e un altro che usa l'oggetto precedente come un collaboratore.

(Parte del punto delle deve essere sensibile a quando il tuo design sta infrangendo le regole, perché spesso indica un punto debole nel design.)

Come regola, non dovremmo aver bisogno di per simulare un metodo pubblico arbitrario, quindi non ti incoraggerei a ottimizzare per quel caso d'uso prematuramente.

    
risposta data 08.08.2018 - 16:19
fonte
1

I'm currently learning about TDD techniques, one of the suggestion is to test only public methods and skip the private ones

Sì, TDD concentra gli sforzi sulla definizione del test tramite il contratto e prima di ottenere l'implementazione in un secondo momento. Definiamo prima i metodi (principalmente pubblici), i loro argomenti, i loro tipi di ritorno e le eccezioni che possono essere lanciati.

Questi contratti potrebbero o potrebbero non provenire da interfaces o virtual methods . Potrebbero essere semplici metodi vuoti di una nuova classe. Quindi, no, non provengono tutti da un'interfaccia . TDD non forzerà il tuo design in questo modo. 1

I have also been reading about Mocking

Ecco dove ti stai perdendo. Mock (IMO) sono una tecnica avanzata per i test. Non direttamente coinvolto con TDD. Puoi ancora seguire TDD senza mock. I mock sono indirizzati per testare componenti con pesanti dipendenze (come ha commentato @ guillamue31 nella sua risposta). fornisce un'istanza "falsa" di una determinata interfaccia. Un comportamento che può essere programmato per soddisfare le condizioni richieste per il caso d'uso in esame. Potrebbe essere anche una lezione. Sì, anche le classi concrete possono essere derise.

When I start developing my application I don't know which methods I will want to mock while creating unit tests, because of that I think it's best to make them all available for mocking.

Bene. Lascia che TDD dica quali componenti sono buoni candidati da prendere in giro. Dopo diverse iterazioni di TDD, una volta che i test diventano ecologici, è più facile vedere quali componenti devono essere disaccoppiati e quindi i candidati devono essere derisi. Ma tieni presente che non tutte le singole dipendenze implicano un'interfaccia. Dipende da te decidere in base a requisiti, esigenze e preferenze.

1: Mi piacerebbe condividere un video di Sandro Mancuso che implementa i kata di Rovers Mars . Vedrai che non ha usato alcuna interfaccia per testare i contratti. Mancuso è molto favorevole a questa metodologia e lo incoraggia a tutti i suoi dipendenti

2: Nota che Sandro sta seguendo TDD e che i metodi di Rover sono protetti !!! Sta giocando con l'ambito del pacchetto. Test e classe concreta sono nello stesso pacchetto. Quindi, TDD non ci obbliga nemmeno a definire metodi come pubblici.

    
risposta data 08.08.2018 - 12:10
fonte
0

No. Hai solo bisogno di interfacce per le classi che vengono utilizzate come dipendenze

ad esempio:

public class Application
{
     private IService service;
     public void Main() {...}
}

public class Service : IService
{
    public Customer GetCustomerForOrder(Order order)
}

public class Customer 
{
    public decimal GetBalance() {...}
}

public class Order 
{
    public decimal GetTotal() {...}
}

Qui solo il servizio deve avere un'interfaccia. Quando testiamo l'applicazione, vorremmo iniettare un servizio mocked e l'interfaccia lo abilita, oltre alla sostituzione generale con implementazioni alternative.

L'applicazione di derisione non ti aiuta affatto.

Cliente e Ordine possono essere testati direttamente in quanto non hanno dipendenze.

    
risposta data 08.08.2018 - 11:48
fonte
-1

Idealmente, dovresti testare tutti i metodi pubblici, il che significa che tutti i metodi pubblici dovrebbero essere sopportabili.

Due to confusion in the comments, I want to point out that with "mockable", I mean "able to be tested". In other words, part of the interface definition (or, as per the suggestion in the question, made virtual, though I'm not a fan of this approach over interfaces)

In realtà, non testerai sempre tutto. Nel mio progetto attuale, ad esempio, ho dovuto lottare solo per avere il tempo di implementare i test unitari (la direzione non lo considerava necessario). Come compromesso, abbiamo deciso di testare solo la logica aziendale che cambia lo stato degli oggetti e di non testare metodi di recupero che recuperano solo i dati.

I assume that making all methods virtual isn't the best solution, then the alternative is the interface approach mentioned above.

Is that the right direction or do I miss something obvious?

Non vedo un motivo per utilizzare l'approccio virtuale. Le interfacce sono già molto più comuni (specialmente se associate a un'iniezione di dipendenza) e coprono già tutte le basi necessarie.

Quindi suggerisco di usare le interfacce invece dei metodi virtuali.

Se qualcuno può mostrarmi un caso valido in cui i metodi virtuali sono migliori delle interfacce, sono felice di sentirlo.

Note
Even if you know you're not going to test certain public methods (such as the data retrieval methods in my current project), you should still include these methods in the interface. Just because you're not testing them today doesn't mean you won't test them tomorrow.

    
risposta data 08.08.2018 - 09:51
fonte

Leggi altre domande sui tag