Zum Inhalt

Mocken von Konstruktoren mit JMockit

Beim Testen mit JMockit bin ich noch auf ein Problem gestossen, das mich eine Zeit lang beschäftigt hat: Wie kann man einen Konstruktor einer Klasse mocken?
Die Dokumentation von JMockit geht darauf nicht speziell ein. Dies liegt wohl daran, dass die Lösung ganz einfach ist. Wenn man aber nicht darauf kommt, nutzt einem dies nichts.

Ganze Klassen mocken

Mockt man ganze Klassen bekommt man das Mocken des Konstruktors gratis dazu. Dazu genügt eine einfache Klasse wie MockedFileOutputStream. Der dazugehörige Test ist sehr einfach:

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import mockit.Mockit;
import org.junit.Test;

public class JMockitConstructorTest 
{

    @Test
    public void testConstructorMocking()
    {
        Mockit.redefineMethods(FileOutputStream.class, MockedFileOutputStream.class);

        try {
            FileOutputStream outStream = new FileOutputStream("DemoString");
            fail("Exception missing");
        } 
        catch (FileNotFoundException e) {
            assertTrue(
                e.toString().equals("java.io.FileNotFoundException: Mocked Exception"));
        }
    }
}

class MockedFileOutputStream
{ 
    public MockedFileOutputStream(String s) throws FileNotFoundException { 
        throw new FileNotFoundException("Mocked Exception");
    }
}

Erneut dient redefineMethods() zum "umhängen" der aufgerufenen Klassen/Methode. Genau wie bei den Methoden funktioniert es auch beim Konstruktor.

Mocken eines statischen Initialisierungsblocks

Ein statischer Initialisierungsblock (static initialization block) ist ein gewöhnlicher Code-Block der mit {} eingeschlossen wird und als static markiert ist. Dieser Block wird vom Klassenlader beim laden der entsprechenden Klasse ausgeführt. Mehr Informationen dazu gibt es in der Insel und bei SUN. Solche Blöcke können mit JMockit durch das überschreiben der Methode $clinit() ersetzt werden. (Die Annotationen @Mock und @MockClass sind für die korrekte Ausführung nötig.)

import static org.junit.Assert.assertTrue;
import org.junit.Test;
import mockit.Mock;
import mockit.MockClass;
import mockit.Mockit;

public class JMockitConstructor 
{
    @Test
    public void TestStaticConstructor()
    {
        Mockit.setUpMocks(MockedClass.class);

        OriginalClass orig = new OriginalClass();
        assertTrue(OriginalClass.number == 0);
    }
}

class OriginalClass
{
    public static int number; // default is 0
    static
    {
        number = 1;
        System.out.println(number);
    }
    public OriginalClass()
    {
        System.out.println("Class OriginalClass created");
    }
    public OriginalClass(String s)
    {
        System.out.println("Class OriginalClass created with " + s );
    }
}

@MockClass(realClass = OriginalClass.class) 
class MockedClass 
{   
    @Mock
    public void $clinit() { 
        System.out.println("mocked static initializer."); 
    }

    @Mock
    public void $init() { 
        System.out.println("mocked default constructor. ");
    }

    public MockedClass() { 
        System.out.println("MockedClass constructed");
    }   
}

// Ausgabe:
MockedClass constructed
mocked static initializer.
MockedClass constructed
mocked default constructor. 

Um solche Blöcke zu mocken braucht es die Methode setUpMocks(). Diese macht im wesentlichen das gleiche wie redefineMethods(), prüft aber auf eine andere Art welches die Mock-Objekte sind. (Siehe API)

Neue Webseite von JMockit

Anfangs Dezember ist JMockit zu Google Code umgezogen. Die neue URL ist https://jmockit.googlecode.com.

Zum Weiterlesen

Im Blog von Dhruba Bandopadhyay fand ich den interessanten Eintrag "JMockit: No holds barred testing with instrumentation over mocking". Dies ist die beste Zusammenfassung zu JMockit die ich bisher ausserhalb der offiziellen Doku gefunden habe.

Wie man Methoden mit JMockit mockt habe ich hier beschrieben.