No Scope registered for scope 'session'

20 lutego 2011, 20:45:32, Patryk Dobrowolski « Generyczne DAO dla Hibernate | Refleksja może być niebezpieczna - zobacz jak zniszczyć sobie życie »

Znowu kilka słów o Springu. Co się stanie, kiedy zdefiniujemy jakiś bean o zasięgu "session" i uruchomimy testy jednostkowe? Wiadomo, że zasięg "session" jest zarezerwowany dla aplikacji J2EE, związany jest z klasami ServletRequest i ServletResponse. Wiadomo również, że przy wykonywaniu testów jednostkowych, kontekst servletu nie jest dostępny. Co się więc stanie i jak temu zaradzić?

Kiedy uruchomimy testy jednostkowe przy takich założeniach, nie zawiedziemy się. Testy się nie wykonają, ale za to zostanie rzucony wyjątek:

java.lang.IllegalStateException: No Scope registered for scope 'session'
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
	at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
	at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:653)
	at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:604)
	at pl.jedenpies.blog.info.BiezacyUzytkownikInfo$$EnhancerByCGLIB$$1f2246a0.setUzytkownik(<generated>)
	at pl.jedenpies.blog.service.AutoryzacjaServiceImpl.autoryzujUzytkownika(AutoryzacjaServiceImpl.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)

I tak dalej.

Rozwiązanie problemu jest niezwykle proste. Spring pozwala zdefiniować własny "scope" dla beanów. Wystarczy więc zarejestrować takowy o nazwie "session", na przykład w taki sposób:

fake-scopes.xml




  
    
      
        
          
        
      
    
  

i voila! Testy przechodzą jak należy.

W przykładzie użyłem zaimplementowanego przez twórców Springa i dostępnego w samej bibliotece scope'a, który utożsamia zasięg (scope) z konkretnym wątkiem. I to pewnie wystarczy w większości przypadków. Oczywiście nic nie stoi na przeszkodzie, aby napisać własną klasę implementująca interfejs org.springframework.beans.factory.config.Scope, co wiąże się z napisaniem zaledwie kilku prostych metod. Wszystko zależy od konkretnego zachowania jakiego oczekujemy po naszym scope

Komentarze

Hoppke, 20 lutego 2011 21:59:43

Ale jeśli w teście jednostkowym wychodzą problemy z takimi zależnościami, to czy nie znaczy to, że test jest "jednostkowy" już tylko z nazwy, bo tak naprawdę testuje działanie kilku jednostek równocześnie?

tmszdmsk, 20 lutego 2011 22:54:22

Jednostkowość tego testu wynika chyba tylko z wykorzystania jUnita.

Patryk, 20 lutego 2011 23:32:39

Przyznaję się, że nie czytałem dotychczas definicji testu jednostkowego i to co zauważył Hoppke, dało mi do myślenia. Dotychczas tego typu testy zawsze nazywałem jednostkowymi i nie spotkałem się z innym określeniem. Nawet jeśli w sensie dosłownym nie testują odizolowanej "jednostki".

Szukałem też na wikipedii lepszego określenia dla tego typu testów i nie znalazłem. Można więc chyba przyjąć, że jeśli mamy przetestowane klasy B i C, od których zależy klasa A, to testowanie klasy A jest testem jednostkowym. Pewne metody klas serwisowych (zwykle to większość nawet) nie są w stanie samodzielnie funkcjonować. Potrzebują dostępu do jakiegoś typu bazy lub repozytorium czy innych zasobów.

Ok, jeśli nie jest to test jednostkowy, to jakiego typu test to jest?

tmszdmsk, 20 lutego 2011 23:44:49

nie wiem czemu komentarz się nie pojawił więc jeszcze raz:
- po pierwsze, test integracyjny
- po drugie, jeśli implementacja klasy B lub C, od której zależą(bezpośrednio lub pośrednio) wszystkie inne w systemie, pojawi się błąd. Wszystkie testy zaczną failować: jak zlokalizujesz błąd?
- po trzecie, polecam poczytać na temat mockowania. Radzę też się zaznajomić z boskimi bibliotekami mockującymi typu polskie mockito

Hoppke, 20 lutego 2011 23:48:55

To pewnie przerośnięty test jednostkowy. Za dużo jak na jednostkę, a pewnie trochę za mało jak na "blackbox" i/lub klasyczny test integracyjny. Może dałoby się to (formalnie) wybronić jako test regresyjny...

Mało która klasa da się przetestować bez zależności czy innych zasobów (jak np. jakieś repozytorium, serwis, fabryka czy inne ustrojstwo), ale w tym momencie pojawiają się np. narzędzia do mockowania -- mockito (polecam), easymock (też nienajgorszy), jmock (odradzam :) -- czy też stuby albo inne "implementacje testowe".

Mocne trzymanie się dzielenia kodu na jednostki i testowanie ich z osobna pomaga uporządkować "design" kodu. No, przynajmniej mi. Bo jeśli aplikacja jest elegancko podzielona na warstwy, logika jest rozbita na komponenty, a całość utkana na siatce interfejsów, to testy jednostkowe z mockami pisze się bardzo przyjemnie. A jeśli trudno jakąś klasę przetestować w ten sposób, to może coś z nią jest nie tak i trzeba ją poprawić...

Patryk, 20 lutego 2011 23:49:35

W tym przypadku akurat nie ma co używać Mockito. A jeśli chodzi o typ testu, to racja. Jest to już test integracyjny. I znów się czegoś nauczyłem.

Zostaw komentarz

Treść:
Podpis:
Strona WWW:
Kod:

Weryfikacja antyspamowa