2012-12-29 21 views
39

Jeśli klasa zawiera kilka metod statycznych, w celu upewnienia się, nikt przez pomyłkę inicjuje wystąpienie tej klasy, zrobiłem prywatny konstruktor:Jak przetestować prywatny konstruktor w aplikacji Java?

private Utils() { 
} 

Teraz .. jak to może być badane, biorąc pod uwagę, że konstruktora nie można zobaczyć? Czy to może być w ogóle test?

+1

Użyj interfejsu API Reflection – MrSmith42

+0

Jak to może działać? Czy masz przykład, proszę? – JAM

+2

Nie. Testujesz metodę używając prywatnego konstruktora. –

Odpowiedz

62

Korzystanie odbicie, to może wywołać prywatny konstruktor:

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

Jednak można zrobić nawet, że nie jest możliwe:

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

Przez rzucanie wyjątek w konstruktorze , zapobiegasz wszystkim próbom.


chciałbym zrobić klasę samą final też po prostu „bo”:

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

+1. Doskonały punkt o wyjątku! – JAM

+1

Nice. Ponadto, jeśli wyjątek został dodany, może istnieć powód do testu jednostkowego również. – msandiford

+3

Uważam, że UnsupportedOperationException jest bardziej odpowiedni niż wyjątek 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

Używam go do zwiększenia zasięgu testowego moich zajęć z narzędziami do 100% – MrSmith42

+1

To jest bardzo fajne! Dziękujemy – JAM

+2

@ MrSmith42 Uzyskanie pokrycia testowego blisko 100% nie jest rzeczą w sobie. Pokrycie testowe jest miarą. A wszelkie działania mające na celu wyłącznie poprawę metryki, a następnie osiągnięcie rzeczywistego wyniku, powodują dewaluację metryki. W końcu możesz mieć cały kod objęty testami, które wykonują wszystkie metody, ale nie weryfikują wyników. Zatem może być sens testowanie prywatnego konstruktora przez bezpośrednie wywoływanie go tylko wtedy, gdy masz do tego silny powód. (Lepiej przetestować go pośrednio, jeśli to możliwe - wywołując metodę fabryczną lub coś w tym rodzaju). W każdym razie, wiedząc, jak to zrobić, jeśli jest to potrzebne, jest dobre :-) –

19

testową intencją kodu .. zawsze :)

Na przykład: Jeżeli punkt Konstruktor, który jest prywatny, nie jest widoczny, więc to, co musisz przetestować, to ten fakt i nic więcej.

Użyj refleksji API do zapytania konstruktorów i sprawdź, czy mają ustawiony atrybut prywatny.

chciałbym zrobić coś takiego:

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

Jeśli chcesz mieć odpowiedni test na budowę obiektu, należy przetestować publicznych API, które pozwala uzyskać zbudowany obiekt. To jest powód, dla którego powinien istnieć API: właściwie zbudować obiekty, więc powinieneś to przetestować :).

+0

Świetny pomysł. Szkodliwe narzędzia do obcinania kodu nie będą wiedzieć, że je przetestowałeś. –

+0

Zobacz https://github.com/trajano/commons-testing dla biblioteki, która robi to i więcej, aby przetestować klasy narzędzi. –

4

Jeśli masz prywatnego konstruktora, jest on wywoływany z nie tak prywatnej metody kodu. Więc przetestujesz tę metodę, a twój konstruktor jest objęty. Nie ma żadnej religijnej cnoty w testowaniu na metodę. Poszukujesz funkcji lub jeszcze lepszego poziomu zasięgu oddziału, i możesz to uzyskać, po prostu wykonując konstruktora przez ścieżkę kodu, która go używa.

Jeśli ta ścieżka kodu jest skomplikowana i trudna do przetestowania, być może trzeba ją zmienić.

5

się upewnić, że nikt przez pomyłkę inicjuje wystąpienie tej klasy

Zazwyczaj to, co robię, jest zmiana metody/konstruktora z prywatnym domyślne widoczność pakietu. I używam tego samego pakietu dla mojej klasy testowej, więc od testu dostępna jest metoda/konstruktor, nawet jeśli nie jest z zewnątrz.

Aby wymusić zasadę aby nie instancję klasy można:

  1. rzut UnsupportedOperationException („nie instancję tej klasy!”) Z domyślnego pustego konstruktora.
  2. zadeklaruj streszczenie klasy: jeśli zawiera tylko metody statyczne, możesz wywoływać metody statyczne, ale nie tworzyć instancji, chyba że je podklasujesz.

lub zastosować obie 1 + 2, nadal można podklasować i uruchomić konstruktora, jeśli test ma ten sam pakiet, co klasa docelowa. Powinno to być całkiem "odporne na błędy"; Złośliwe koderzy zawsze znajdzie obejście :)

3

Jeśli dodać wyjątek w konstruktorze takich jak:

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

Wezwanie constructor.newInstance() w klasie testowej wygeneruje InvocationTargetException zamiast swojego UnsupportedOperationException, ale żądany wyjątek będzie zawarty w wyrzuconym.
Jeśli chcesz zgłosić wyjątek od swojego wyjątku, możesz rzucić cel wyjątku wywołania, gdy tylko zostanie przechwycony wyjątek wywołania.
Na przykład, przy użyciu JUnit 4 można to zrobić:

@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

Nie. Konstruktor jest prywatny. To wszystko, czego potrzebujesz. Java egzekwuje swoją prywatność.

Nie testuj platformy.

+0

Praktycznym przypadkiem użycia w celu przetestowania niedostępnego kodu jest uzyskanie 100% pokrycia testowego, aby nikt nie musiał ponownie oglądać tej klasy. Jeśli zasięg jest zatrzymany na poziomie 95%, wielu programistów może próbować znaleźć przyczynę tego problemu, aby wielokrotnie wpadać na ten problem. Również, Java ssie w egzekwowaniu prywatności. :) – thisismydesign

Powiązane problemy