zur Startseite

Mock Creator Version 0.2.4


 Home
 Kontakt
+Kompetenz
+Projekte
-MockCreator
  Eclipse 2
  VisualAge 4
  Standalone
  Version 0.1
  Download
 Xpedition
 Impressum
 Feedback


 deutschenglish



download zip      english text

Juli 2003: MockCreator hat seine neue Heimat bei Sourceforge gefunden. Neuere Releases sind nur noch dort verfügbar.

Der Mock Creator wurde ursprünglich von Christian Junghans bei abstrakt entwickelt. Er dient dazu in Java Mockobjekte aus Interfaces zu generieren. Er ist verfügbar für

"frühere" News

26. August 2002
Version 0.2.4 mit Verbesserungen für Eclipse und Bugfix für alle Versionen ist veröffentlicht:

  • Class Dateien selbst aus jar-Archiven können verwendet werden um MockObjekte zu generieren.
  • Mehrdimensionale Arrays in Parametern und Rückgabewerten werden jetzt korrekt verarbeitet.

12. August 2002
Version 0.2.3 mit folgenden Verbesserungen für Eclipse ist veröffentlicht:

  • Die GUI lässt sich nun durch ziehen vergrössern, sodass auch lange Packagenamen dargestellt werden könnnen

08. August 2002
Version 0.2.2 mit folgenden Verbesserungen für Eclipse ist veröffentlicht:

  • Generierung von MockObjekten für Projekte, die nicht unter ECLIPSE_HOME/workspace liegen

Mailingliste

Diskussionen, Feedback und Bugreports bitte an die Mailingliste auf Sourceforge

Der Mockcreator ist freie Software, wir übernehmen keine Garantie für irgendwelche Probleme die sich aus seiner Nutzung ergeben. Selbstverständlich helfen wir aber gern bei Fragen und Problemen.

Was sind MockObjekte?

MockObjekte dienen dem isolierten (Unit-)Test von Komponenten und Klassen. Sie "simulieren" Objekte, die in einem Test verwendet werden.

Dieser Artikel führt nur in die Bedienung und Installation des MockCreators ein - er liefert keine Einführung in die Arbeit mit MockObjects. Eine diesbezügliche Dokumentation befindet sich z.B. unter www.mockobjects.com.

Schnittstelle der generierten MockObjekte

Hier ein Beispiel für generierte Mockobjekte für das Interface Foo:

public interface Foo {
  String doSomething( String something );
  double squareRoot( double input );
}

Hier die generierte Klasse:

public class MockFoo extends de.abstrakt.mock.MockObject implements Foo {
  // Methoden aus dem Interface Foo
  public String doSomething( java.lang.String something )  {...}
  public double squareRoot( double input )  {...}

  // setup MockObject: Setzen der Erwartungen,
  // ggf. mit Rückgabewert oder zu werfender Exception.
  public void expectDoSomething( String something, String returnValue )  {...}
  public void expectDoSomething( String something, Throwable throwable )  {...}
  public void expectSquareRoot( double input, double returnValue ) {...}
  public void expectSquareRoot( double input, Throwable throwable )  {...}

  // setup MockObject: Angegebene Methoden bei Aufruf nicht beachten.
  // ggf. mit Rückgabewert oder zu werfender Exception.
  public void setDoSomethingDummy( Throwable throwable )  {...}
  public void setDoSomethingDummy( String returnValue )  {...}
  public void setSquareRootDummy( Throwable throwable )  {...}
  public void setSquareRootDummy( double returnValue )  {...}
}

Wichtige geerbte Methoden aus de.abstrakt.mock.MockObject:

package de.abstrakt.mock;
public class MockObject {
  public void startBlock()  {...}
  public void endBlock()  {...}
  public void verify()  {...}
  public Boolean checkDummy( Boolean flag, boolean value )  {...}
  [...]
}

Einfache Verwendung

Der Mock Creator erstellt Mockobjekte, die das gewählte Interface implementieren. Im Test werden die Aufrufe die auf den Mockobjekten erwartet werden in folgender Form angegeben. Wir erwarten in diesem Fall, dass auf einem Object Foo die Methode doSomething mit dem Parameter "nobody expects" aufgerufen wird. Als Folge dieses Aufrufs gibt das MockObject das Ergebnis "the spanish inquisition" zurück. Wird hingegen squareRoot(...) oder doSomething mit einem anderen Parameter aufgerufen, so wirft MockFoo eine Exception.

// setup des MockObjects
MockFoo mockFoo = new MockFoo();
mockFoo.expectDoSomething( "nobody expects", "the spanish inquisition" );
// Testcode aus der Testklasse
assertEquals( "the spanish inquisition", mockFoo.doSomething( "nobodyExpects" ) );
// Verifizierung
mockFoo.verify();

Erweiterte Features

Durch die Verwendung von MockObjects in Tests sowie die einfache Verfügbarkeit von MockImplementationen ist es sehr einfach möglich, Tests zu schreiben, die eine Änderung des zu testenden Codes massiv erschweren - schliesslich schreiben die Mockobjekte jeden einzelnen Aufruf genau vor. Hier gibt es mehrere Abhilfen... wir raten dazu, alle zu verwenden :-)

  • MockObjects vorsichtig einsetzen
  • Dummyaufrufe parametrisieren
  • Blöcke von Code mit beliebiger Reihenfolge

MockObjects vorsichtig einsetzen

Wahrscheinlich muss jeder einmal die Schwierigkeiten durchlaufen haben, Mock-getesteten Code umzuschreiben (refaktorisieren) um dieses Problem auch selbst zu sehen... ;-)

Dummyaufrufe parametrisieren

Die vom MockCreator generierten Klassen ermöglichen es, einige Aufrufe als "unerheblich für den Testverlauf" zu kennzeichnen - ein guter Kandidat hierfür sind beispielsweise einfache getXY-Methoden. Dadurch wird der tatsächliche Aufruf dieser Methoden nicht überwacht:

// setup für das MockObject
MockCustomer mockCustomer = new MockCustomer();
mockCustomer.setDummyGetName( "Otto Normalverbraucher" );
mockCustomer.expectGetBalance( new Money( 10 ) );

// Erzeugen der zu testenden Klasse und Test
ThisClassIsTested testee = new ThisClassIsTested( mockCustomer );
testee.thisMethodIsTested();

// Testcode aus der zu testenden Klasse
public class ThisClassIsTested {
  private Customer _customer;
  public ThisClassIsTested( Customer customer ) { _customer = customer; }
  public void thisMethodIsTested() {
    doSomething( _customer.getName() );
    doSomethingElse( _customer.getName() );
    Money balance = _customer.getBalance();
    System.out.println( "Balance for Customer " + _customer.getName()
      + ": " + balance + " Euro" );
  }
  [...]
}

// Verifizierung
mockCustomer.verify();

Blöcke von Code in beliebiger Reihenfolge

Das Standardverhalten der generierten MockObjects ist, dass die Reihenfolge der Methodenaufrufe beachtet wird. Es ist jedoch möglich, dieses Standardverhalten zu ändern - und zwar auch für einzelne "Methodenblöcke":

// setup für das MockObject
MockFoo mockFoo = new MockFoo();
mockFoo.expectDoSomething( "nobody expects", "the spanish inquisition" );
mockFoo.startBlock();
mockFoo.expectSquareRoot( 4, 2 );
mockFoo.expectSquareRoot( 9, 3 );
mockFoo.expectSquareRoot( 16, 4 );
mockFoo.endBlock();
// Testcode aus der Testklasse 
// Testcode aus der zu testenden Klasse
public class ThisClassIsTested {
  private Foo _foo;
  public ThisClassIsTested( Foo foo ) { _foo = foo; };
  public void thisMethodIsTested() {
		_foo.doSomething( "nobody expects" ) );
		System.out.println( "sqrt(16) is " + _foo.squareRoot( 16 ) );
		System.out.println( "sqrt(4) is "  + _foo.squareRoot( 4 ) );
		System.out.println( "sqrt(9) is "  + _foo.squareRoot( 9 ) );
  }
  [...]
}
// Verifizierung
mockFoo.verify();

Generierte Objekte in Kürze

Jeder Aufruf einer expect-Methode (hier expectGet) speichert ein Objekt-Array mit Methodenname und Paramaetern in einer ExpectationList -> mockobjects und den Rückgabewert (hier "Eintrag 1") in eine Liste von Rückgabewerten.

Jeder Methodenaufruf vom zu testenden Objekt auf dem Mockobjekt vergleicht den Aufruf mit dem zuvor im Test als erwartet angegebenen Aufruf.

Um auch Fehlerfälle testen zu können generiert der MockCreator zu jeder Methode im Interface zwei expect-Methoden in der folgenden Form (hier für die Methode "methodCall"):

public void expectMethodName
               ( [alle Parameter]
                 [, Rückgabewert sofern
                         nicht void]
               );
public void expectMethodName
               ( [alle Parameter]
                 [, Throwable der bei Aufruf
                         geworfen wird]
               );

Mit Hilfe der zweiten Version ist es möglich, auch einen Fehlerfall im Test zu simulieren. Die Mock-Implementation gibt in diesem Fall bei Aufruf keinen Rückgabewert zurück, sondern löst die übergebene Exception aus. Hierbei ist die Schnittstelle zu beachten: Sofern nicht im Interface eine Exception deklariert ist, können hier nur RuntimeExceptions und Errors angegeben werden, da sonst während des Tests eine ClassCastException auftritt. Wenn das Interface Exceptions deklariert, können natürlich auch die deklarierten Exceptions als Werte für den Parameter Throwable verwendet werden.

Wenn die Anzahl von Methodenaufrufen auf einem Objekt unerheblich ist, dient ein Aufruf der setDummyXY-Methoden dem Festlegen der entsprechenden Rückkabewerte bei Aufruf der Methode.

public void setDummyMethodCall( [Rückgabewert sofern nicht void] );
public void setDummyMethodCall( [Throwable, der bei Aufruf geworfen wird] );

Blöcke von Code, deren Aufrufreihenfolge unerheblich ist, lassen sich mit startBlock und endBlock festlegen.

public void startBlock();
public void endBlock();

Features

  • Neu in Version 0.2: Versionen für Visual Age 4, Eclipse 2, Kommandozeile
  • Neu in Version 0.2: Blöcke von Methodenaufrufen deren Reihenfolge gleichgültig ist kennzeichnen
  • Generiert vollständige Mockobjekte auch von Interface-Hierarchien
  • Generiert zu jedem Methodenaufruf eine das Interface implementierende Methode und zwei expect Methoden s.u.
  • Richtigkeit der Methodenaufrufe kann automatisch über verify überprüft werden
  • Wenn der Sourcecode im Repository zur Verfügung steht, werden die Namen der Parameter verwendet
  • Wenn der Sourcecode im Repository nicht zur Verfügung steht (Import eines class-Files), wird das Mockobjekt aufgrund der im Bytecode verfügbaren Definition generiert.
  • Umsonst + Open Source

Geplante Features für nächste Releases

  • Mehr Dokumentation
  • Parametrisierung: Generierung der Methodennamen auch nach com.mockobject.*-Standard.

Lasst uns doch wissen welche davon ihr für sinnvoll haltet oder was ihr sonst vermisst

Support, Kontakt

Der Mockcreator ist freie Software, wir übernehmen keine Garantie für irgendwelche Probleme die sich aus seiner Nutzung ergeben. Selbstverständlich helfen wir aber gern bei Fragen und Problemen.

Kontakt, Feedback und Bugreports bitte über die Mailingliste auf Sourceforge

Bekannte Schwierigkeiten, offene Punkte

  • Das Speichern der generierten Klassen in Visual Age dauert leider sehr lange, grade bei umfangreicheren Interfaces.
  • Der MockCreator arbeitet nur für Interfaces in definierten Packages - nicht für solche ohne bzw. im "default Package".
  • Die generierten Klassen folgen (aus historischen Gründen) unserem eigenen Benennungsschema und nicht dem in com.mockobjects.* etablierten (expect... statt setExpected... etc.)
  • In Eclipse/Linux gibt es Probleme: Die Schreibweise für mockcreator.jar unterscheidet sich in plugin.xml und Dateisystem, ferner gibt es Probleme bei der Generierung unter Linux (wir arbeiten dran).

Lizenz

Der MockCreator (Code in de.abstrakt.tools.codegeneration.*) steht unter der GNU General Public License (GPL), die zu importierenden Klassen im Package de.abstrakt.mock stehen unter der GNU Lesser General Public License (LGPL). Dadurch sind die generierten Klassen auch in "closed source"-Projekten verwendbar.
Die von unserer Software verwendete Software (com.mockobjects.*) ist hiervon nicht betroffen. Sie muss separat bezogen werden und steht (derzeit) unter der Apache-Lizenz.

Und hier noch der Standardtext:

Copyright (C) 2001/2002 abstrakt gmbh

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.