2016-08-13 2 views
1

Ich habe meine Klasse alsWie sich ändern Werte von „private static final String filePath“ für Unittesting

public class MappingLoader 
    { 
    private static final String filepath = "/tmp/mapping.properties" // unix path of production system 
    private static Map<String,String> mapping = new HashMap<String,String>() 

    static 
    { 
     loadMappingFile() 
    } 


@VisibleForTesting  
static void loadMappingFile() 
    { 
     //reading properties files here 

    final Properties prop = new Properties(); 
     try (final InputStream input = Files.newInputStream(Paths.get(filepath))) 
     { 
      prop.load(input); 
     } 
     catch (final Exception e) 
     { 
      ... 
      ... 
      throw new RuntimeException(e); 
     } 

       //now load "mapping" from properties file 
       .... 
        .... 
     } 
    } 

Zum Testen, ich brauche den Wert der String-Variable „filepath“ so ändern, dass sie die Entwicklung nehmen sollte Systempfad (zB c: \ project \ target \ mapping.properties)

Ich habe versucht Powermocks in Junits, aber es wirft immer Ausnahme und beendet.

Anmerkungen auf Klassenebene:

@RunWith(PowerMockRunner.class) 
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit") 

und in Testfall:

Whitebox.setInternalState(Mappingloader.class, "filepath", testfilepath); 
Mappingloader.loadMappingFile(); 

Ich habe auch versucht, in (Change private static final field using Java reflection) als gegeben dies durch Reflexion zu ändern, aber es wirft immer die FileNotFoundException für " Dateipfad "und nimmt nicht den geänderten Pfad" testFilePath "

Gibt es eine Möglichkeit, ich kann diese Variable so ändern, dass es FileNo nicht wirft tFoundException ohne Änderung im Quellcode?

Wenn ich "neue RuntimeException (e) löschen" entfernen im Quellcode funktioniert Powermocks für mich. Aber ich möchte dies erreichen, ohne Quellcode zu ändern, entweder über Powermock, Reflection API.

Antwort

3

Nun, Sie können Ihr Glück mit Powermock versuchen; und das sollte funktionieren (vielleicht, wenn du noch ein paar Stunden damit verbringst, seine Dokumentation zu lesen und Experimente zu machen); Aber ehrlich gesagt: Dein Problem testet nicht. Ihr Problem besteht darin, dass Sie nicht testbaren Code erstellt haben. Und jetzt versuchst du, den großen powermock Hammer zu benutzen, um zu reparieren, was dein gebrochenes Design ist.

Sie sehen, wenn Sie statische Methoden und Konstanten verwenden; die Leute denken, sie "sparen" bei der Leistung (was wahr ist; aber zu einem sehr kleinen Grad; was wahrscheinlich nicht für 99,999% aller Anwendungen von Bedeutung ist); aber sie vergessen immer wieder, dass die Verwendung von statischedirekten Kopplung von verschiedenen Funktionalitäten führt. statische ist eine Anomalie in guten OO-Design; und sollte mit großer Sorgfalt verwendet werden.

interface MappingLoader { 
    Map<String, String> loadMappingsFrom(String fileName); 
} 

class MappingLoaderImpl implements MappingLoader { 
    ... 

und Sie sehen, ganz plötzlich nur mit „echten“ Schnittstellen und Klassen beschäftigen;:

also in Ihrem Fall könnten Sie das Ganze mit etwas in diese Richtung ersetzen und nicht-statische Methoden; und Überraschung: Jetzt können Sie das Ganze vollständig testen; und höchstwahrscheinlich brauchen Sie nicht einmal einen spöttischen Rahmen. Sie erstellen einfach eine temporäre Datei mit Mappings. und dann stellen Sie sicher, dass Ihre impl-Klasse Ihnen die Zuordnungen aus dieser Datei gibt. Zero mocking; Null-Test von internen Implementierungsdetails; nur wenige behaupten.

Und ein weiterer Vorteil auf: Sie alle Ihre Client-Code, der sollte nur die MappingLoader Schnittstelle verwenden können auch getestet werden. Weil gewöhnliche Frameworks wie EasyMock oder Mockito es Ihnen ermöglichen, Instanzen dieser Schnittstelle nachzuahmen ... weil: keine statischen Aufrufe mehr!

So ändern Sie den Wert von privaten endgültigen statischen Feldern - indem Sie sie nicht verwenden!

(und wenn ich dich neugierig: watch this zu lernen, wie prüfbar Code von Anfang an schreiben)

+1

Ausgezeichnete Antwort! Ich stimme völlig zu, wie man testbaren Code entwickelt. Und noch mehr: Sobald Sie eine instanziierbare, wiederverwendbare und testbare Abstraktion haben, um eine Eigenschaftendatei zu lesen, gibt es nichts dagegen, eine statische Fassade zum Lesen einer bestimmten Datei zu verwenden. (Wenn die Abstraktion vollständig getestet wurde, muss die Fassade nicht getestet werden.) –

+0

Gern geschehen. – GhostCat

+0

@GhostCat, alles, was Sie gesagt haben, sind gut denkt und wie es sein sollte. Aber Sie haben nicht gesagt, wie es erreicht werden könnte, wenn ein Entwickler bereits solchen Code hat und vielleicht ein Teil der Bibliothek ist, der nicht geändert werden kann. Natürlich sollte ein Entwickler all diesen Prinzipien folgen, vermeiden Sie statische und so weiter, und so weiter, aber wenn ein Projekt von Grund auf neu gestartet wird. Wenn Code nicht durch Test abgedeckt wird - make change, ist es keine gute Idee. –

1

Während ich mit @ GhostCat Antwort völlig einverstanden, ich verstehen Sie suchen nach einer Lösung nicht beteiligt um den Quellcode zu ändern. Haben Sie daran gedacht, den Inhalt von /tmp/mapping.properties vor dem Test zu ändern (und sie später wiederherzustellen)?

+0

Dies ist eine einfache und recht vernünftige Idee. –

+0

@ Rogério Danke, mein Freund. –

0

static final String Felder oder andere final static Grundelemente Felder können nicht in der Laufzeit geändert werden. Wenn Sie ehrlich sind, können Sie diese Felder ändern, aber Ihre Änderung wirkt sich nicht auf Code aus, der diese Felder verwendet, da die Referenz während der Kompilierzeit durch den Wert ersetzt wird.

Mein Vorschlag: Verwenden Sie static mock zu Files.newInputStream() Anruf und dann ByteArrayInputStream mit erwarteten Daten zurück. In diesem Fall vermeiden Sie einen fragilen Festplatten-IO-Betrieb, der die Teststabilität beeinträchtigen könnte.

+0

Disk IO (von einem lokalen Dateisystem wie in diesem Fall, zumindest) ist keine fragile Operation, und es sollte nicht verspottet werden. Jedes Mal, wenn wir ein Java-Programm ausführen, kommt es immer wieder zu Festplatten-IO. –

Verwandte Themen