2011-12-15 14 views
9

mam klasę użytkownika, takich jak:Jak kontrolować jednostki kontrolne, które korzystają z usługi springSecurityService?

class User { 
    transient springSecurityService 
    String displayName 
    String password 
<snip> 
    protected void encodePassword() { 
     password = springSecurityService.encodePassword(password) 
    } 
} 

a UserController. Staram się pisać testy jednostkowe w UserController jednak dostaję ten błąd na zapisywanie, aktualizowanie i usuwanie testy:

java.lang.NullPointerException: Cannot invoke method encodePassword() on null object 

Co muszę skonfigurować w sposób szyderczy uzyskać to praca?

Próbowałem wiele kombinacji kpiącego kodu, takich jak następujące, ale jestem w braku.

defineBeans { 
    springSecurityService(SpringSecurityService) 
} 

Każda rada byłaby mile widziana.

+0

mam przeżywa dokładnie ten sam problem. O ile widzę, defineBeans() powinien działać, zgodnie z dokumentacją. Niestety nie ma to żadnego wpływu. –

Odpowiedz

0

Zrobiłem to tak:

protected void encodePassword() { 
     // SpringSecutiryService is not injected in tests. 
     if (springSecurityService) 
      password = springSecurityService.encodePassword(formPassword) 
    } 
+2

To nie jest idealne rozwiązanie, wygląda raczej jak poprawka, aby uzyskać testy. Co się stanie, jeśli usługa bezpieczeństwa zostanie usunięta z jakiegoś dziwnego powodu w produkcji ... Wtedy wszystkie twoje hasła nie zostaną zahartowane (bez komunikatu o błędzie) – david

+0

Masz rację @david. –

8

ja osobiście nie lubię dodawanie logiki do produkcji kodu, aby pomóc spełnić kryterium. Czasami musisz podjąć decyzję, co najlepiej zrobić. Kilka opcji ...

  1. Powyższa odpowiedź będzie działać, ale jak już zaznaczono Ja osobiście nie woleliby
  2. nie testują urządzenie. Napisz wszystkie swoje testy, które napotkasz w tej sytuacji jako testy integracyjne.
  3. Wykop to z fałszywą usługą.

Jeśli ten kod (lub kod, który napotyka ten sam problem) jest rozpylany w całej aplikacji, to prawdopodobnie będziesz chciał wymyślić sposób na wyodrębnienie tych połączeń w testach jednostkowych dla wszystkich przypadków testowych, aby nie powielamy wysiłków konfiguracyjnych wszędzie. Łatwym sposobem na poznanie tego jest metaClassing.

@Test 
public void something() { 
    def user = ... 
    def springSecurityService = new Object() 
    springSecurityService.metaClass.encodePassword = {String password -> "ENCODED_PASSWORD"} 
    user.springSecurityService = springSecurityService 
    ... 
} 

Teraz, gdy zostanie wywołana springSecurityService.encodePassword powinien wrócić "ENCODED_PASSWORD". Tworzę także Object zamiast tworzyć nowy SpringSecurityService, ponieważ jeśli utworzysz prawdziwą usługę, możesz zakończyć nieoczekiwanie i nieświadomie wywoływanie rzeczywistych metod w tej usłudze i wykonanie testów z niewłaściwych powodów. Wolałbym nie mieć takiego błędu metody niż test przechodzący, który nie powinien przemijać.

+0

Dobra odpowiedź! Przy okazji poprawiłem wiersz "service.metaClass.encodePassword =" na "springSecurityService.metaClass.encodePassword =" – david

-1

W moim przypadku próbowałem zastąpić implementację encodePassword() SecUser, która wywołuje metodę springSecurityService.encodePassword().

Byłem zaskoczony, bo musiałem zastąpić klasę i instancji (jeśli nie przesłonić, to się nie powiedzie):

SecUser.metaClass.encodePassword = { 'a' } 
user.metaClass.encodePassword = { 'b' } 

jakiś pomysł, dlaczego muszę to?

0

Uważam, że słuszne jest wyrwanie usługi. Będziesz chciał przetestować różne przypadki, które mogą być wartością zwracaną i że poprawna wartość jest przekazywana do metody usługi.

@Test 
public void something() { 
    def user = ... 
    def expectedPassword = 'mock encoded pass' 
    controller.springSecurityService = [encodePassword: { String passwd -> return expectedPassword }] 

    ... 
} 

lub

@Test 
public void something() { 
    def user = ... 
    def expectedPassword = 'mock encoded pass' 
    def mockSecurityService = mockFor(SpringSecurityService) 
    mockSecurityService.demand.encodePassword { String passwd -> return expectedPassword} 
    controller.springSecurityService = mockSecurityService.createMock() 

    ... 
    mockSecurityService.verify() // throws exception if demands aren't met 
} 
Powiązane problemy