2015-09-06 13 views
6

Ich programmiere in einer Java SE-Umgebung mit WELD-SE für Abhängigkeitsinjektion. Deshalb Abhängigkeiten einer Klasse wie folgt aussehen:Wie injiziert Mocks beim Testen von Klassen mit CDI in der Produktion

public class ProductionCodeClass { 
    @Inject 
    private DependencyClass dependency; 
} 

Wenn eine Unit-Test für diese Klasse zu schreiben ich für DependencyClass ein Mock bin die Schaffung und wie ich will keine vollständige CDI-Umgebung für jeden Test starten Ich betreibe ich das Mock „injizieren“ manuell:

import static TestSupport.setField; 
import static org.mockito.Mockito.*; 

public class ProductionCodeClassTest { 
    @Before 
    public void setUp() { 
     mockedDependency = mock(DependencyClass.class); 
     testedInstance = new ProductionCodeClass(); 
     setField(testedInstance, "dependency", mockedDependency); 
    } 
} 

die statisch importierte Methode setField() ich mich mit Werkzeugen in einer Klasse geschrieben habe ich in Tests verwenden:

public class TestSupport { 
    public static void setField(
           final Object instance, 
           final String field, 
           final Object value) { 
     try { 
      for (Class classIterator = instance.getClass(); 
       classIterator != null; 
       classIterator = classIterator.getSuperclass()) { 
       try { 
        final Field declaredField = 
           classIterator.getDeclaredField(field); 
        declaredField.setAccessible(true); 
        declaredField.set(instance, value); 
        return; 
       } catch (final NoSuchFieldException nsfe) { 
        // ignored, we'll try the parent 
       } 
      } 

      throw new NoSuchFieldException(
         String.format(
          "Field '%s' not found in %s", 
          field, 
          instance)); 
     } catch (final RuntimeException re) { 
      throw re; 
     } catch (final Exception ex) { 
      throw new RuntimeException(ex); 
     } 
    } 
} 

Was mir an dieser Lösung nicht gefällt, ist, dass ich diesen Helfer in jedem neuen Projekt immer wieder brauche. Ich habe es bereits als Maven-Projekt gepackt, das ich als Testabhängigkeit zu meinen Projekten hinzufügen kann.

Aber ist nicht etwas fertig gemacht in einer anderen gemeinsamen Bibliothek fehlt mir? Irgendwelche Kommentare zu meiner Art, dies generell zu tun?

+0

Konstruktorinjektion verwenden. – chrylis

Antwort

7

Mockito unterstützt diese aus der Box:

public class ProductionCodeClassTest { 

    @Mock 
    private DependencyClass dependency; 

    @InjectMocks 
    private ProductionCodeClass testedInstance; 

    @Before 
    public void setUp() { 
     testedInstance = new ProductionCodeClass(); 
     MockitoAnnotations.initMocks(this); 
    } 

} 

Die @InjectMocks Annotations Injektion von Klassen auslöst oder Grenzflächen in der Testklasse getäuscht in diesem Fall DependencyClass:

Mockito versucht einzuspritzen nach Typ (mit Namen in Fällen gleichen Typen). Mockito wirft nichts, wenn die Injektion fehlschlägt - Sie müssen die Abhängigkeiten manuell erfüllen.

Hier verwende ich auch die @Mock Annotation anstelle von mock() anrufen. Sie könnten immer noch mock() verwenden, aber ich bevorzuge die Verwendung von Anmerkungen.

Als Randnotiz stehen Ihnen Reflexionswerkzeuge zur Verfügung, die die in TestSupport implementierte Funktionalität unterstützen. Ein solches Beispiel ist ReflectionTestUtils.

+0

Ja, genau das war es, wofür ich mich einsperrte. Habe es schon ausprobiert und die Ergebnisse sehen sehr gut aus. Ich konnte das nicht finden ... ;-) Ich benutze nicht Spring, also nur hinzufügen, um ReflectionTestUtils zu bekommen, könnte ein bisschen schwergewichtig sein. Aber danke auch für diesen Vorschlag. –

+0

@Magnilex: Haben Sie darüber nachgedacht, entweder "@RunWith (MockitoJUnitRunner.class)" oder "@Rule public MockitoRule rule ..." zu Ihrem Code hinzuzufügen? – Henrik

+0

@ Henrik Der Code ist abgeschlossen. '@RunWith (MockitoJUnitRunner.class)' wäre jedoch eine Alternative. Diese Antwort erklärt jedoch die Frage von OP: Die Frage fragt nicht nach Alternativen für die Ausführung eines Mockito-basierten JUnit-Tests. – Magnilex

1

Alternative, wenn mockitos in Funktionen baut nicht ausreicht: Versuchen needle4j.org

Es ist eine Injektion/Mock-Framework, das die Injektion von Mocks und konkreten Instanzen ermöglicht und unterstützt auch postConstruct für Lifecycle-Simulation.

public class ProductionCodeClassTest { 

    @Rule 
    public final NeedleRule needle = new NeedleRule(); 

    // will create productionCodeClass and inject mocks by default 
    @ObjectUnderTest(postConstruct=true) 
    private ProductionCodeClass testedInstance; 

    // this will automatically be a mock 
    @Inject 
    private AServiceProductionCodeClassDependsOn serviceMock; 

    // this will be injected into ObjectUnderTest 
    @InjectIntoMany 
    private ThisIsAnotherDependencyOfProdcutionCodeClass realObject = new ThisIsAnotherDependencyOfProdcutionCodeClass(); 

    @Test 
    public void test_stuff() { 
     .... 
    } 

} 
+0

Schön! Gibt es zufällig ein Beispielprojekt/eine Testsuite?Etwas wie JMockits Beispiel (https://github.com/jmockit/jmockit1/tree/master/samples/petclinic) wäre ideal. –

+0

Nun, es gibt ein paar Beispiele basierend auf 2.2, aber wir haben sie bisher nicht auf 2.3 migriert. Aber es könnte hilfreich sein: https://github.com/needle4j/needle/tree/master/src/examples –