2013-09-05 2 views
8

Próbuję testów jednostkowych z ramą Play w scala. Pisałem klasę, która sprawdza, czy konfiguracja jest poprawna (Mam obsługi błędu nieco więcej, ale ja faktycznie użyć tego kodu do mojego testu teraz):Testy weryfikacyjne Mockito scala nie działają (gra w ramy)

class TaskQueueConfig(conf: Configuration) { 
    val schedulingEnabled = conf.getBoolean("schedulingEnabled").get 
    val processingEnabled = conf.getBoolean("processingEnabled").get 
    val queueName = conf.getString("queue").get 
} 

Jestem testowania to za pomocą odtwarzać 2.1. 1 w konfiguracji domyślnej Test:

class ConfigTestSpec extends Specification with Mockito with CalledMatchers { 
    "TaskQueueConfig" should { 
    "verify calls" in { 
     val tqConf = mock[Configuration] 
     tqConf.getString("queue") returns Some("queueName") 
     tqConf.getBoolean("schedulingEnabled") returns Some(true) 
     tqConf.getBoolean("processingEnabled") returns Some(true) 
     Logger.error("setup done") 

     val config = new TaskQueueConfig(tqConf) 

     there was one(tqConf).getString("queue") 
     there were two(tqConf).getBoolean(any[String]) 
     there were one(tqConf).getBoolean("schedulingEnabled") 
     there were one(tqConf).getBoolean("processingEnabled") 
    } 
    } 
} 

dostaję następujący błąd:

[error] x verify calls 
[error] The mock was not called as expected: 
[error] configuration.getString$default$2(); 
[error] Wanted 1 time: 
[error] -> at config.ConfigTestSpec$$anonfun$2$$anonfun$apply$4$$anonfun$apply$12.apply(ConfigTestSpec.scala:61) 
[error] But was 2 times. Undesired invocation: 
[error] -> at config.TaskQueueConfig.<init>(TaskQueueConfig.scala:10) (ConfigTestSpec.scala:61) 

jest to bardzo dziwne, ponieważ kod jest bardzo odizolowany i nie ma wyraźnie tylko 1 conf .getString call w TaskQueueConfig. Linia 10 to linia z metodą getString. Linia 61 to linia z "był jeden (tQConf) .getString"

Jak mogę rozwiązać ten problem?

(nie ma różnicy między byłem i był).

PS: Wiem, że ten przykład jest dość bezużyteczny do testowania, ale mam bardziej złożone konfiguracje, w których istnieją pewne reguły, które należy przetestować.

Sposób aktualizacji 1 getString ma dwa parametry, drugi parametr ma wartość domyślną brak (to typ jest wariant [Zestaw [String]]). Kiedy jawnie dodaję None do konfiguracji i weryfikacji, to nadal nie działa. Ale kiedy dodaję null zamiast tego, działam.

val tqConf = mock[Configuration] 

    tqConf.getString("queue", null) returns Some("queueName") 
    tqConf.getBoolean("schedulingEnabled") returns Some(true) 
    tqConf.getBoolean("processingEnabled") returns Some(true) 

    val c = new TaskQueueConfig(tqConf) 

    there was one(tqConf).getString("queue", null) 
    there was one(tqConf).getString(any[String], any[Option[Set[String]]]) 
    there were two(tqConf).getBoolean(any[String]) 
    there was one(tqConf).getBoolean("schedulingEnabled") 
    there was one(tqConf).getBoolean("processingEnabled") 

    c.processingEnabled must beTrue 
    c.schedulingEnabled must beTrue 
    c.queueName must be("queueName") 

Sądzę więc, że teraz pytanie brzmi: dlaczego muszę używać wartości NULL?

+0

Czemu weryfikacji co nazwano na pozornie configs vs tylko sprawdzenie stanu końcowy utworzonego obiektu? Jeśli zgasłeś poprawnie, powinieneś po prostu sprawdzić, czy wartości właściwości 'TaskQueueConfig' odpowiadają ustawionym wartościom skrótowym. Wiem, że to nie odpowiada na twoje pytanie, ale z pewnością wyeliminowałoby to ten problem ... – cmbaxter

+0

W tym konkretnym przypadku, oczywiście, ale pochodzę z większego przykładu, w którym to miałem, i zawęziłem to do najmniejszy możliwy przykład, więc proszę, zostańcie ze mną! – Jaap

+0

Moje dwie wstępne przemyślenia dotyczyły tego, że policzono samą krępę (co spowodowało, że były to dwa połączenia) lub miałeś problem z określeniem zakresu i kolejna sekwencja testów również była zliczana, a więc dodatkowe wywołanie funkcji 'getString'.Sam przetestowałem pierwszą teorię i nie wygląda mi to tak, jakby mockito przypadkowo obejmowało upieranie się w liczbie połączeń. Jeśli chodzi o problem z zakresu, jeśli twój prawdziwy kod jest taki, jaki jest tutaj, a sztuczka nie jest tworzona globalnie, to nie powinno to być problemem. – cmbaxter

Odpowiedz

4

Musisz użyć null w dwóch argumentów getString() rozmowy ponieważ Mockito wymaga, aby użyć użytku dopasowujących dla wszystkich argumentów lub wcale.

Tak więc mieszanie "queue" (literał) z any[Option[Set[String]]] (a matcher) nie będzie działać. Czasami Mockito może pracować, że zrobiłeś to i to daje błąd, ale wydaje się, że masz pecha tutaj ...

Spróbuj użyć dopasowujących we wszystkich pozycjach, takich jak ten:

tqConf.getString(org.mockito.Matchers.eq("queue"), any[Option[Set[String]]]) returns Some("queueName") 

i weryfikacja:

there was one(tqConf).getString(eq("queue"), any[Option[Set[String]]]) 
+0

Dzięki! Mam jedno inne podobne pytanie do tego, czy możesz wyjaśnić, dlaczego tqConf.getString ("queue", None) zwraca Some ("queueName") nie działa? Kiedy to robię, otrzymuję NPE w kodzie, który wywołuje getString ("kolejka"). Get Żadna nie jest domyślną wartością drugiego opcjonalnego parametru getString ... – Jaap

+1

Myślę, że trafiłeś naprawdę interesująco przypadek rogu dla Mockito w porównaniu do domyślnych argumentów Scala. Testowałem kombinacje, a jedyne, które działają, to: 'tqConf.getString (" queue ") zwraca Some (" queueName ")' (tj. Bezpośrednio używając dokładnie jednego literału, ignorując domyślnie arg całkowicie) i 'tqConf .getString (org.mockito.Matchers.eq ("kolejka"), dowolna [Option [Set [String]]]) zwraca Some ("queueName") '(tj. używając matchers dla _both_ argumentów, tak jak zalecenie Mockito). Wciąż wolałbym drugą formę za oczywistość, ale jest interesująca ... – millhouse