2016-09-21 1 views
0

Ich muss einen Test schreiben PartitionMapperTest für meine Java-Klasse PartitionMapper. Diese Klasse hat private Felder mit @Inject Annotationen, aber sie hat nur einen No-Param-Konstruktor.Injizieren private Felder mit @Inject während des Tests?

Für den Test möchte ich einen partitionMapper vor jedem Test erstellen und Werte in seine privaten Felder injizieren. Dann testet der Tester die Methode des Zuordners mapPartitions und gibt Werte an. Ich weiß jedoch nicht, wie man diese Werte in partitionMapper injiziert.

PartitionMapper.java

@Named 
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper { 

    @Inject 
    private JobContext jobContext; 

    @Inject 
    @BatchProperty 
    private String fetchSize; 

    @Inject 
    @BatchProperty 
    private String rowsPerPartition; 

    // other batch properties ... 

    @PersistenceUnit(unitName = "h2") 
    private EntityManagerFactory emf; 

    @Override 
    public PartitionPlan mapPartitions() throws Exception { 
     // ... 
    } 
} 

PartitionMapperTest.java

public class PartitionMapperTest { 

    private PartitionMapper partitionMapper; 

    @Before 
    public void setUp() { 
     // Prepare JobContext, batch properties to inject ... 

     // Instantiation 
     partitionMapper = new PartitionMapper(); 

     // TODO How to inject these objects into partitionMapper? 
    } 

    @Test 
    public void testMapPartitions() throws Exception { 
     PartitionPlan partitionPlan = partitionMapper.mapPartitions(); 
     for (Properties p : partitionPlan.getPartitionProperties()) { 
      // Assertions here ... 
     } 
    } 

    // ... 
} 

Ich habe tatsächlich ein echtes PartitionMapperTest auf Mockito und PowerMock Basis zu implementieren, die auf meinem GitHub zu sehen sind. Das Problem ist, dass es so viele Annahmen gibt, dass es zu sehr schlechtem Code für das Benutzerverständnis führt. Ich suche nach einer anderen Lösung für das Refactoring.

Antwort

2

Gibt es einen Grund, nur einen args Konstruktor zu haben?

Ich würde Ihnen empfehlen, Konstruktor-Injektion anstelle von Feldinjektion zu verwenden. Das würde dein Problem lösen.

Zum Beispiel statt:

public class Foo { 
    @Inject 
    private Bar bar; 
} 

dies tun:

public class Foo { 
    private Bar bar; 

    public Foo(@Inject Bar bar) { 
     this.bar = bar; 
    } 
} 

Wenn Sie Ihre Injektionspunkte auf diese Weise definieren Sie haben eine saubere API und Ihre Klasse kann in nicht-cdi-Umgebungen verwendet werden, (wie Unit-Tests).

Es gibt viele Ressourcen über "Erbauereinspritzung VS-Feldeinspritzung" ... Auch auf stackoverflow, z. https://stackoverflow.com/a/19382081/4864870.

+0

Sie Antwort ist eine gute Idee. Ich werde es wahrscheinlich wählen. Aber ich bin gespannt, wie CDI die Werte in private Felder einbringt. Ich meine, es gibt keine Setter, wie schaffen sie das? Wenn ich den gleichen Mechanismus anwende, kann ich das auch erreichen? (Injizieren Sie sie selbst) –

+0

Ich möchte nur einen Konstruktor ohne Argumente haben, weil ich nicht möchte, dass irgendjemand diese interne Klasse in der API instanziiert. Aber sie können es immer noch tatsächlich tun:/ –

+1

Feldinjektionen erfordern Reflektion, soweit ich weiß. Ich nehme an, dass es so gemacht wird: 'Field field = Beispiel.class.getDeclaredField (" fieldName "); field.setAccessible (true); field.set (Instanz, Wert); '. Wenn Sie erzwingen möchten, dass Ihre Klasse nur intern verwendet wird, können Sie Ihr Konstruktorpaket als privat festlegen. Das ist keine 100% ige Lösung, aber besser als nichts. Wenn Sie dies tun, stellen Sie sicher, dass Ihr Test im selben Paket ist ...;) –

0

Verwendung geschützte Felder statt privaten Lage sein, Felder in Unit-Test zu verspotten:

@Named 
public class PartitionMapper implements javax.batch.api.partition.PartitionMapper { 

    @Inject 
    JobContext jobContext; 

    @Inject 
    @BatchProperty 
    String fetchSize; 

    @Inject 
    @BatchProperty 
    String rowsPerPartition; 

    // other batch properties ... 

    @PersistenceUnit(unitName = "h2") 
    EntityManagerFactory emf; 

    @Override 
    public PartitionPlan mapPartitions() throws Exception { 
     // ... 
    } 
} 
+0

Eigentlich kann ich sie im privaten Bereich mit Mockito und PowerMock verspotten. Und ich möchte meinen Quellcode nicht ändern, um meinen Test anzupassen. Und ich denke du hast meine Frage missverstanden. Ich habe früher zu viele Mocks gemacht, also möchte ich jetzt auf den Injektionsansatz umstellen, ohne zu spotten. Irgendeine Idee, wie man es erreicht? –

Verwandte Themen