2013-07-18 19 views
12

Sto creando un JUnit TestCase per un progetto che deve caricare un file di configurazione durante l'inizializzazione.getResourceAsStream da JUnit

Questo file di configurazione si trova all'interno del progetto nella cartella src/main/resources/config e durante il build maven lo inserisce nella cartella/config, all'interno del JAR.

La classe di inizializzazione, legge il file da lì con questa dichiarazione:

ClassLoader classloader = this.getClass().getClassLoader(); 
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream("/config/config.xml"))); 

Il problema che ho è che quando schiero ed eseguire questo vaso nel server di applicazione funziona come previsto, tuttavia, ogni volta che eseguirlo in un JUnit TestCase all'interno di Eclipse, il metodo getResrouceAsStream restituisce null.

Considerando che la classe è my.package.MyClassTest.java e che vive in src/test/java/my/package/MyClassTest.java, ho già provato a inserire una copia del file config.xml nel seguendo le cartelle senza successo:

- src/test/resources/config 
- src/test/resources/my/package/config 
- src/test/java/my/package/config 

so che simili questioni sono state poste molte volte qui a StackOverflow, ma tutte le risposte che ho trovato si riferisce a cambiare il modo in cui il file viene caricato e, anche se cambiando il codice può essere un opzione, preferirei trovare il posto giusto per il file, quindi non ho bisogno di modificare cose che già funzionano nell'ambiente di produzione.

Quindi, dove devo posizionare questo file per poterlo utilizzare nel mio test JUnit?

UPDATE

Ho appena si avvicinò con la soluzione con un piccolo cambiamento nel codice: Invece di utilizzare la ClassLoader per ottenere la risorsa, ho usato direttamente la classe:

Class clazz = this.getClass(); 
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(clazz.getResourceAsStream("/config/config.xml"))); 

E legge il file correttamente da src/test/resources/config/config.xml.

Tuttavia, c'è qualcosa di molto strano qui: Il metodo Class.getResourceAsStream è:

public InputStream getResourceAsStream(String name) { 
    name = resolveName(name); 
    ClassLoader cl = getClassLoader0(); 
    if (cl==null) { 
     // A system class. 
     return ClassLoader.getSystemResourceAsStream(name); 
    } 
    return cl.getResourceAsStream(name); 
} 

E se il debug è, chiaramente posso vedere che questo getClassLoader0() restituisce esattamente lo stesso oggetto (stesso id) della chiamata precedente, this.getClass(). getResourceAsStream() (che ho mantenuto, solo per confrontare i valori) !!!

Cosa sta succedendo qui ?!

Perché chiamare direttamente il metodo non funziona, mentre si inserisce una nuova chiamata di metodo tra le opere?

Onestamente, sono davvero stupito di fronte a questo.

BTW, sto usando la versione 4.10 di JUnit. Potrebbe essere la manomissione della chiamata getClassLoader in qualche modo?

Molte grazie,

Carles

+1

'src/test/resources /' una cartella di origine in Eclipse? –

+0

@SotiriosDelimanolis se pubblichi questo come risposta, lo voterò. –

+0

@SotiriosDelimanolis: Sì, lo è. In effetti, quando l'ho guardato mi sono reso conto che Eclipse lo prende di default per i progetti di Maven. –

risposta

14

Rispondendo alla tua domanda come

E se metto a punto, posso vedere chiaramente che questo getClassLoader0() restituisce esattamente lo stesso oggetto (lo stesso id) rispetto al chiamata precedente, this.getClass(). getResourceAsStream() (che ho mantenuto, solo per confrontare i valori) !!!

Cosa sta succedendo qui ?!

Perché chiamare direttamente il metodo non funziona, mentre si inserisce una nuova chiamata di metodo tra le opere?

La differenza tra chiamare

this.getClass().getClassLoader().getResourceAsStream("/config/config.xml"); 

e chiamando

this.getClass().getResourceAsStream("/config/config.xml"); 

sta nella fonte precisa che si stava mostrando da Class:

public InputStream getResourceAsStream(String name) { 
    name = resolveName(name); 
    ClassLoader cl = getClassLoader0(); 
    if (cl==null) { 
     // A system class. 
     return ClassLoader.getSystemResourceAsStream(name); 
    } 
    return cl.getResourceAsStream(name); 
} 

Ma il problema non è con cosa getClassLoader0() restituisce. Restituisce la stessa cosa in entrambi i casi. La differenza è in realtà in resolveName(name). Questo è un metodo privato nella classe Class.

private String resolveName(String name) { 
    if (name == null) { 
     return name; 
    } 
    if (!name.startsWith("/")) { 
     Class<?> c = this; 
     while (c.isArray()) { 
      c = c.getComponentType(); 
     } 
     String baseName = c.getName(); 
     int index = baseName.lastIndexOf('.'); 
     if (index != -1) { 
      name = baseName.substring(0, index).replace('.', '/') 
       +"/"+name; 
     } 
    } else { 
     name = name.substring(1); 
    } 
    return name; 
} 

Quindi, vedete, prima di richiamare getResourceAsStream() del classloader, in realtà rimuove la barra a partire dal percorso.

In generale, proverà a ottenere la risorsa relativa a this quando non ha una barra e la passerà al classLoader se ha una barra all'inizio.

Il metodo getResourceAsStream() del classLoader è in realtà destinato a essere utilizzato per percorsi relativi (altrimenti si utilizzerà semplicemente un FileInputStream).

Quindi, quando si utilizzava this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");, in realtà si passava il percorso con una barra all'inizio, che non riusciva. Quando hai usato this.getClass().getResourceAsStream("/config/config.xml"); è stato abbastanza gentile da rimuoverlo per te.

+0

Ho paura di non poterlo testare attualmente (vorrei provare a rimuovere manualmente la prima barra dal percorso e vedere se funziona allo stesso modo), ma sembra convincente. Grazie e accettato. –

1

non sono sicuro di questo, ma si potrebbe provare a mettere la cartella risorse nel/albero principale src invece dell'albero src/test. In alcune configurazioni, almeno, eclipse copia i file 'risorse' da src in classi, ma non da test a classi. Vale la pena di un colpo ...

+0

Entrambe le cartelle delle risorse esistono in src/main e src/test, contenenti gli stessi file ... ma non funziona. –

2

getResourceAsStream la funzione ClassLoader non rimuoverà la barra preceduta dalla stringa di ricerca poiché la ricerca verrà eseguita rispetto al classpath. Cerca la risorsa dove il percorso di ricerca utilizzato per caricare le classi.

diciamo per esempio se la classe yourpackage/Test.class, che si trova sotto /a/b/c/d/yourpackage/Test.class, caricato dal classloader sistema (cioè predefinito classloader) e classpath deve indicare /a/b/c/d per caricare la classe.La ricerca verrà effettuata su questo percorso.

getResourceAsStream La funzione dell'oggetto Classe rimuove la barra preceduta dalla stringa di ricerca poiché la ricerca verrà eseguita rispetto alla classe in cui si trova. Cerca la risorsa da cui viene caricata la tua classe.

Dire per esempio se yourpackage/Test.class caricato dal /a/b/c/d/yourpackage/Test.class poi il percorso delle risorse sarà /a/b/c/d/yourpackage/config/config.xml

È possibile verificare questo utilizzando il codice seguente snipet poiché sia ​​getResource e getResourceAsStream utilizzato per seguire lo stesso algoritmo per la ricerca.

System.out.println(Test.class.getClassLoader().getResource("config/config.xml")); 
System.out.println(Test.class.getResource("config/config.xml")); 
Problemi correlati