2012-12-29 14 views
39

Se una classe contiene una serie di metodi statici, al fine di assicurarsi che nessuno per errore inizializza un'istanza di questa classe, ho fatto un costruttore privato:Come testare un costruttore privato in applicazione Java?

private Utils() { 
} 

Ora .. come potrebbe questo essere testato, dato che il costruttore non può essere visto? Questo può essere coperto da test?

+1

Utilizzare l'API di Reflection – MrSmith42

+0

Come potrebbe andare? Hai un esempio per favore? – JAM

+2

Non lo fai. Si prova il metodo usando il costruttore privato. –

risposta

62

utilizzando la riflessione, si può invocare un costruttore privato:

Constructor<Util> c = Utils.class.getDeclaredConstructor(); 
c.setAccessible(true); 
Utils u = c.newInstance(); // Hello sailor 

Tuttavia, si può fare anche questo non è possibile:

private Utils() { 
    throw new UnsupportedOperationException(); 
} 

Con un'eccezione nel costruttore , prevedi tutti i tentativi.


vorrei fare la classe stessa final troppo, solo "perché":

public final class Utils { 
    private Utils() { 
     throw new UnsupportedOperationException(); 
    } 
} 
+7

+1. Ottimo punto sull'eccezione! – JAM

+1

Bello. Inoltre, se è stata aggiunta l'eccezione, potrebbe esserci effettivamente un motivo per avere un test unitario anche per questo. – msandiford

+3

Trovo che UnsupportedOperationException sia più appropriato di IllegalStateException. –

6
@Test 
public// 
void privateConstructorTest() throws Exception { 
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors(); 
    // check that all constructors are 'private': 
    for (final Constructor<?> constructor : constructors) { 
     Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers())); 
    }   
    // call the private constructor: 
    constructors[0].setAccessible(true); 
    constructors[0].newInstance((Object[]) null); 
} 
+0

Lo uso per aumentare la copertura di prova delle mie classi di utils al 100% – MrSmith42

+1

Questo è molto bello! Grazie – JAM

+2

@ MrSmith42 Ottenere una copertura di prova vicino al 100% non è una cosa-in-sé. La copertura del test è una metrica. E qualsiasi azione intesa unicamente a migliorare la metrica piuttosto che a raggiungere il risultato effettivo svaluta la metrica. Dopotutto è possibile avere tutto il codice coperto da test che esercitano tutti i metodi ma non verificano i risultati. Quindi, può essere sensato testare il costruttore privato chiamandolo direttamente solo se si ha una forte ragione per farlo. (Meglio testarlo indirettamente se possibile, chiamando il metodo factory o qualcosa di simile). In ogni caso, sapere come farlo se necessario è buono :-) –

19

prova l'intento del codice .. sempre :)

Ad esempio: Se il punto della costruttore essendo privato è quello di non essere visto, allora ciò che è necessario per testare è questo fatto e nient'altro.

Utilizzare la riflessione API per interrogare per i costruttori e convalidare che hanno impostare l'attributo privato.

vorrei fare qualcosa di simile:

@Test() 
public void testPrivateConstructors() { 
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors(); 
    for (Constructor<?> constructor : constructors) { 
     assertTrue(Modifier.isPrivate(constructor.getModifiers())); 
    } 
} 

Se si vuole avere una prova adeguata per la costruzione di un oggetto, è necessario verificare l'API pubblica che consente di ottenere l'oggetto costruito. Questa è la ragione detta API dovrebbe esistere: per costruire correttamente gli oggetti in modo che si dovrebbe verificare per questo :).

+0

Ottima idea. Strumenti di copertura del codice troppo brutti non sapranno che l'hai provato. –

+0

Vedere https://github.com/trajano/commons-testing per una libreria che fa questo e altro per testare le classi di utilità. –

4

Se si dispone di un costruttore privato, viene chiamato da un metodo non proprio privato del codice. Quindi testerai quel metodo e il tuo costruttore è coperto. Non c'è virtù religiosa nell'avere un test per metodo. Stai cercando la funzione o il livello di copertura della filiale ancora migliore, e puoi farlo semplicemente esercitando il costruttore attraverso il percorso del codice che lo usa.

Se il percorso del codice è complicato e difficile da testare, forse è necessario un refactoring.

5

per assicurarsi che nessuno per errore inizializza un'istanza di questa classe

Di solito quello che faccio, è quello di cambiare il metodo/costruttore da privato a visibilità pacchetto di default. E io uso lo stesso pacchetto per la mia classe di test, quindi dal test il metodo/costruttore è accessibile, anche se non proviene dall'esterno.

Per applicare il criterio di non creare un'istanza della classe è possibile:

  1. tiro UnsupportedOperationException ("non un'istanza di questa classe!") Dal costruttore vuoto di default.
  2. dichiara l'abstract della classe: se contiene solo metodi statici, è possibile chiamare i metodi statici ma non istanziarlo, a meno che non lo si sottoclassi.

o applicare entrambi 1 + 2, è ancora possibile eseguire sottoclasse ed eseguire il costruttore se il test condivide lo stesso pacchetto della classe di destinazione. Questo dovrebbe essere abbastanza "a prova di errore"; codificatori maligni troveranno sempre una soluzione :)

3

Se si aggiunge un'eccezione nel costruttore come ad esempio:

private Utils() { 
    throw new UnsupportedOperationException(); 
} 

L'invocazione constructor.newInstance() nella classe di test genera un InvocationTargetException al posto del tuo UnsupportedOperationException, ma la l'eccezione desiderata sarà contenuta in quella generata.
Se si desidera far valere l'eccezione , è possibile gettare la destinazione dell'eccezione di chiamata, una volta che è stata rilevata l'eccezione di chiamata.
Ad esempio, utilizzando JUnit 4 si potrebbe fare questo:

@Test(expected = UnsupportedOperationException.class) 
public void utilityClassTest() throws NoSuchMethodException, IllegalAccessException, InstantiationException { 
    final Constructor<Utils> constructor = Utils.class.getDeclaredConstructor(); 
    constructor.setAccessible(true); 
    try { 
     constructor.newInstance(); 
    } catch (InvocationTargetException e) { 
     throw (UnsupportedOperationException) e.getTargetException(); 
    } 
} 
-1

Non. Il costruttore è privato. Questo è tutto ciò di cui hai bisogno. Java rafforza la sua privacy.

Non testare la piattaforma.

+0

Un pratico caso d'uso per testare il codice altrimenti inaccessibile è di ottenere una copertura di prova del 100% in modo che nessuno debba guardare di nuovo a quella classe. Se la copertura è bloccata al 95%, molti sviluppatori potrebbero tentare di capirne il motivo solo per incontrare questo problema più e più volte. Inoltre, Java fa schifo per rafforzare la privacy. :) – thisismydesign

Problemi correlati