Classe concreta di derisione - Non raccomandato

11

Ho appena letto un estratto del libro "Growing Object-Oriented Software" che spiega alcuni motivi per cui il mocking della classe concreta non è raccomandato.

Ecco alcuni esempi di codice di un test unitario per la classe MusicCentre:

public class MusicCentreTest {
  @Test public void startsCdPlayerAtTimeRequested() {
    final MutableTime scheduledTime = new MutableTime();
    CdPlayer player = new CdPlayer() { 
      @Override 
      public void scheduleToStartAt(Time startTime) {
        scheduledTime.set(startTime);
      }
    }

    MusicCentre centre = new MusicCentre(player);
    centre.startMediaAt(LATER);

    assertEquals(LATER, scheduledTime.get());
  }
}

E la sua prima spiegazione:

The problem with this approach is that it leaves the relationship between the objects implicit. I hope we've made clear by now that the intention of Test-Driven Development with Mock Objects is to discover relationships between objects. If I subclass, there's nothing in the domain code to make such a relationship visible, just methods on an object. This makes it harder to see if the service that supports this relationship might be relevant elsewhere and I'll have to do the analysis again next time I work with the class.

Non riesco a capire esattamente cosa intende quando dice:

This makes it harder to see if the service that supports this relationship might be relevant elsewhere and I'll have to do the analysis again next time I work with the class.

Capisco che il servizio corrisponda al metodo di MusicCentre chiamato startMediaAt .

Che cosa intende con "altrove"?

L'estratto completo è qui: link

    
posta Mik378 19.11.2012 - 14:33
fonte

3 risposte

4

This makes it harder to see if the service that supports this relationship might be relevant elsewhere and I'll have to do the analysis again next time I work with the class.

Dopo aver riflettuto a lungo su questo argomento, ottengo una possibile interpretazione di questa citazione:

Il "servizio" indicato corrisponde al "fatto di programmazione". Ciò potrebbe essere espresso da un'interfaccia ben definita e "focalizzata su un ruolo" denominata "ScheduledDevice" o espressa implicitamente da un'implementazione concreta del metodo che non dipende da alcuna interfaccia.

Nell'esempio sopra, la pianificazione è espressa dall'intero oggetto completo di nome CDPlayer . Pertanto, porta ancora alla relazione implicita tra MusicCentre e "fact of scheduling".

Quindi, se iniziamo a iniettare classi concrete e le prendiamo in giro in oggetti di alto livello; quando vogliamo testare questi, dobbiamo analizzare ogni oggetto "concreto" iniettato per vedere se presenta una relazione specifica che dobbiamo MAMMARE perché sono NASCOSTI (impliciti). Al contrario, la codifica SEMPRE sull'interfaccia permette allo sviluppatore di capire direttamente quale tipo di relazione sta per essere servita dall'oggetto di alto livello e quindi di rilevare le caratteristiche che devono essere prese in giro per isolare il test unitario.

    
risposta data 19.11.2012 - 16:13
fonte
6

L'autore di quel post sta promuovendo l'uso delle interfacce sull'uso delle classi membro.

It turns out that my MusicCentre object only uses the starting and stopping methods on the CdPlayer, the rest are used by some other part of the system. I'm over-specifying my MediaCentre by requiring it to talk to a CdPlayer, what it actually needs is a ScheduledDevice.

La relazione che è preoccupata di riscoprire in seguito è il fatto che la classe MediaCentre non ha bisogno di tutto l'oggetto CdPlayer. La sua affermazione è che usando un'interfaccia (presumibilmente limitata a solo start | stop) è più facile capire cos'è realmente l'interazione.

"altrove" significa semplicemente che altri oggetti possono avere relazioni limitate allo stesso modo e l'oggetto membro completo non è richiesto - un sottoinsieme della funzionalità racchiusa tramite un'interfaccia dovrebbe essere sufficiente.

Il reclamo inizia ad avere più senso quando esplodi tutte le potenziali funzionalità:

  • avviare
  • arresto
  • pausa
  • record
  • ordine di riproduzione casuale
  • tracce di esempio, inizio della canzone
  • sample tracks, sample random of song
  • fornire informazioni multimediali
  • ...

Ora la sua affermazione di "Ho solo bisogno di start & stop" ha più senso. L'utilizzo dell'oggetto membro concreto anziché di un'interfaccia rende meno chiaro agli sviluppatori futuri quanto è richiesto veramente . Eseguire test unitari da MediaCentre su tutte le altre funzioni all'interno di CdPlayer è uno spreco di sforzi di test dal momento che appartengono allo stato "non mi interessa". Se la funzione Record non funzionava in questo caso, non ci interessa perché non è necessaria. Ma un futuro manutentore non lo saprebbe necessariamente in base al codice, come scritto.

In definitiva, la premessa dell'autore è di utilizzare solo ciò che è necessario e chiarire ai futuri manutentori quanto era necessario prima. L'obiettivo è minimizzare la rilavorazione / rianalizzare il modulo di codice durante la successiva manutenzione.

    
risposta data 19.11.2012 - 16:19
fonte
3

Il servizio che intendevo qui era CDPlayer.scheduleToStartAt (). Questo è ciò che chiama il MediaCentre: il collaboratore di cui ha bisogno per funzionare. Il MediaCentre è l'oggetto in esame.

L'idea è che se faccio esplicito solo ciò che dipende da MediaCentre, non una classe di implementazione, posso dare un nome a quel ruolo di dipendenza e parlarne. Tutto quello che il MediaCentre deve sapere è che parla con ScheduledDevices. Poiché il resto del sistema cambia, non sarà necessario modificare MediaCentre a meno che le sue caratteristiche non cambino.

Ti aiuta?

    
risposta data 19.11.2012 - 16:58
fonte

Leggi altre domande sui tag