Domanda

Questa è una domanda generale su come testare un'unità di una classe Java usando oggetti finti.
Posso riassumere il mio problema con questo esempio.
Diciamo che ho un'interfaccia chiamata MyInterface.java e un " TwoString " L'oggetto che non sostituisce equals ()

" TwoString.java "

   private String string1;
   private String string2;

   public TwoString(String string1, String string2) {
     this.string1 = string1;
     this.string2 = string2;
   }
   ...getters..setters..

" MyInterface.java "

void callMe(TwoString twoString);

Quindi ho " MyClass.java " Oggetto. Il suo costruttore accetta un'implementazione concreta di MyInterface.
MyClass methodToTest () contiene la logica per creare in qualche modo un'espulsione TwoString. Diciamo che verrà creato come

new TwoString("a","b")

Quindi quando viene chiamato methodToTest () crea questo oggetto TwoString che verrà passato al metodo Interface callMe (TwoString twoString) .

In pratica voglio deridere l'interfaccia. Crea un oggetto MyClass con questo mock. Quindi verifica che il metodo mock venga chiamato con un'istanza specifica di TwoString.

Sto usando EasyMock e questo è un po 'di codice java

" MyClassTest.java "

public void test() throws Exception {   
   MyInterface myInterfaceMock = createMock(MyInterface.class);
   MyClass myClass = new MyClass(myInterfaceMock);

   myInterfaceMock.callMe(new TwoString("a","b"));   <--- fails here
   expectLastCall();
   replay(myInterfaceMock);

   myClass.methodToTest();
   verify(myInterfaceMock);

Ecco il problema. L'oggetto TwoString che mi aspetto nella chiamata

myInterfaceMock.callMe(new TwoString("a","b"));

è diverso da quello generato in MyClass.methodToTest () perché TwoString.java non sostituisce gli uguali.

Posso saltare il problema sull'istanza TwoString usando

myInterfaceMock.callMe((TwoString)anyObject());

ma voglio essere sicuro che il metodo di interfaccia venga chiamato con un'istanza specifica di TwoString che contiene " a " come string1 e " b " come stringa2.

In questo caso l'oggetto TwoString è molto semplice e sarà facile sovrascrivere il metodo equals - ma cosa succede se devo controllare un oggetto più complesso.

Grazie

modifica:

Proverò a renderlo più leggibile con questo esempio

public class MyClassTest {
    private MyClass myClass;
    private TaskExecutor taskExecutorMock;

    @Before
    public void setUp() throws Exception {
        taskExecutorMock = createMock(TaskExecutor.class);
        myClass = new MyClass(taskExecutorMock);
    }

    @Test
    public void testRun() throws Exception {
        List<MyObj> myObjList = new ArrayList<MyObj>();
        myObjList.add(new MyObj("abc", referenceToSomethingElse));

        taskExecutorMock.execute(new SomeTask(referenceToSomethingElse,  ???new SomeObj("abc", referenceToSomethingElse, "whatever")));   <--- ??? = object created using data in myObjList
        expectLastCall();
        replay(taskExecutorMock);

        myClass.run(myObjList);

        verify(taskExecutorMock);
    }
}

??? SomeObj = oggetto creato da myClass.run () utilizzando i dati contenuti in myObjList.
Diciamo che SomeObj proviene da una libreria di terze parti e non sovrascrive uguale.

Voglio essere sicuro che il metodo taskExecutorMock.execute () venga chiamato con un'istanza specifica di SomeObj

Come posso verificare che myClass.run () stia effettivamente chiamando il metodo taskExecutorMock con un'istanza corretta

È stato utile?

Soluzione

È possibile utilizzare un metodo di corrispondenza uguale personalizzato utilizzando org.easymock.IArgumentMatcher .

Dovrebbe assomigliare a:

private static <T extends TwoString> T eqTwoString(final TwoString twoString) {
    reportMatcher(new IArgumentMatcher() {
        /** Required to get nice output */
        public void appendTo(StringBuffer buffer) {
            buffer.append("eqTwoString(" + twoString.getString1() + "," + twoString.getString2() + ")");
        }

        /** Implement equals basically */
        public boolean matches(Object object) {
            if (object instanceof TwoString) {
                TwoString other = (TwoString) object;
                // Consider adding null checks below
                return twoString.getString1().equals(object.getString1()) && twoString.getString2().equals(object.getString2());
            }
            else {
                return false;
            }
        }
    });
    return null;
}

E viene utilizzato come segue:

myInterfaceMock.callMe(eqTwoString(new TwoString("a","b")));

Alcuni dettagli potrebbero non essere corretti, ma in sostanza è come l'ho fatto prima. C'è un altro esempio e spiegazioni più approfondite disponibili nella documentazione di EasyMock . Cerca IArgumentMatcher .

Altri suggerimenti

Primo piano: probabilmente intendi "sovrascrivere equivale a" piuttosto che implementare, dal momento che tutte le classi hanno un po ' l'implementazione di uguali (quella che ereditano da Object se non sovrascrivono nient'altro).

La risposta in questo caso è semplice: tutti gli oggetti valore dovrebbero davvero implementare equals e hashcode. Che si tratti di uno semplice come TwoString o dell'oggetto più complesso a cui alludi, dovrebbe essere responsabilità dell'oggetto verificare se è uguale a qualche altro oggetto.

L'unica altra alternativa sarebbe fondamentalmente decostruire l'oggetto nel tuo codice di test, quindi invece di

assertEquals(expected, twoStr);

lo faresti

assertEquals(expected.getStringOne(), twoStr.getStringOne());
assertEquals(expected.getStringTwo(), twoStr.getStringTwo());

Spero che tu possa vedere che questo è male in almeno tre modi. In primo luogo, stai praticamente duplicando la logica che dovrebbe essere nel metodo equals () della classe; e dovunque tu voglia confrontare questi oggetti dovrai scrivere lo stesso codice.

In secondo luogo, puoi vedere solo lo stato pubblico dell'oggetto, potrebbe esserci uno stato privato che fa sì che due apparentemente oggetti simili non siano uguali (ad esempio una classe Lift potrebbe avere un accesso accessibile pubblicamente " floor ", ma anche privato" salendo o scendendo "state).

Infine, è una violazione della Legge di Demetra che una classe di terze parti stia fondamentalmente scherzando con gli interni di TwoString che cercano di capire se le cose sono uguali.

L'oggetto stesso dovrebbe implementare il proprio metodo equals () - puro e semplice.

Dai un'occhiata a Jakarta Commons Lang: EqualsBuilder.reflectionEquals ()

Mentre sono d'accordo con dtsazza che tutti gli oggetti valore dovrebbero avere un metodo equals () (e hashCode () ), sono non sempre appropriato ai test: la maggior parte degli oggetti valore baserà l'uguaglianza su una chiave, piuttosto che su tutti i campi.

Allo stesso tempo, sono sospettoso di qualsiasi test che voglia controllare tutti i campi: mi sembra che stia dicendo & assicurati che questo metodo non abbia cambiato qualcosa che non avevo intenzione di cambiare . " Che è un test valido, e ad un certo livello un test molto ben intenzionato, ma è un po 'spaventoso che ne senti il ??bisogno.

  

In questo caso l'oggetto TwoString è molto semplice e sarà facile sovrascrivere il metodo equals - ma cosa succede se devo controllare un oggetto più complesso.

Una volta che i tuoi oggetti iniziano a diventare così complessi che non puoi banalmente verificare se sono uguali da altrove, probabilmente dovresti rifattorizzare e iniettarli come dipendenza. Ciò cambierebbe il design, ma di solito è in meglio.

Sembra anche che tu faccia affidamento su una certa conoscenza del comportamento interno delle tue classi. Quanto sopra è un test di interazione tra due classi, che funziona comunque, ma più grande diventa il tuo set di componenti testati, meno puoi davvero parlare ancora di " unit " test. A un certo punto lasci il regno dei test unitari e inizi a fare test di integrazione, nel qual caso potresti stare meglio con un cablaggio di test completo e un comportamento isolante in determinati luoghi ...

Puoi ottenerlo con i captator di argomenti in Mockito 1.8.

http: // mockito .googlecode.com / svn / rami / 1.8.0 / javadoc / org / Mockito / Mockito.html # 15

So che stai usando EasyMock ma passare a Mockito è facile ed è molto meglio!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top