2011-12-15 4 views
9

У меня есть класс пользователей, например, такие:Как модульные контрольные контроллеры, которые используют SpringSecurityService?

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

И в UserController. Я пытаюсь писать тесты для UserController Однако я получаю эту ошибку для сохранения, обновления и удаления тестов:

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

Что я должен настроить на пути насмешливый, чтобы получить эту Работа?

Я пробовал много комбинаций издевательского кода, такого как следующее, но я в недоумении.

defineBeans { 
    springSecurityService(SpringSecurityService) 
} 

Любые советы были бы весьма признательны.

+0

Я испытываю точно такой же вопрос. Насколько я вижу, defineBeans() должен работать, согласно документации. К сожалению, это не имеет никакого эффекта. –

ответ

0

Я сделал это так:

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

Это не идеальное решение, больше похоже на исправление для прохождения тестов. Что делать, если служба безопасности обходится по какой-то нечетной причине в процессе производства ... Тогда все ваши пароли не будут хешированы (без сообщения об ошибке) – david

+0

Вы правы @david. –

8

Я лично не нравится добавлять логику производства кода, чтобы помочь удовлетворить тест. Иногда вам нужно принять решение о том, что лучше делать. Пара вариантов ...

  1. выше ответ будет работать, но, как я уже говорил, я лично не предпочел бы
  2. Не модульное тестирование. Напишите все ваши тесты, которые вступают в эту ситуацию в качестве интеграционных тестов.
  3. Отметьте это с помощью поддельного сервиса.

Если этот код (или код, который сталкивается с той же проблемой) посыпается во всем приложении, то вы, вероятно, захотите выяснить способ извлечения этих вызовов в модульных тестах для всех тестовых случаев, чтобы вы «Не дублируйте ваши усилия по настройке повсюду. Легкий способ издеваться над этим - это метаклассирование.

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

Теперь, когда springSecurityService.encodePassword вызывается, он должен вернуть «ENCODED_PASSWORD». Я также создаю Object вместо создания экземпляра нового SpringSecurityService, потому что, если вы создаете экземпляр фактического сервиса, вы можете неожиданно и неосознанно вызвать фактические методы этой службы и пройти тесты по неправильным причинам. Я бы предпочел получить такую ​​ошибку метода, чем тест прохождения, который не должен проходить.

+0

Хороший ответ! Btw Я исправил строку "service.metaClass.encodePassword =" до "springSecurityService.metaClass.encodePassword =" – david

-1

В моем случае я попытался переопределить реализацию encodePassword() SecUser, которая вызывает springSecurityService.encodePassword().

Я был удивлен, потому что мне нужно переопределить класс и экземпляр (если я не переопределит, он терпит неудачу):

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

любая идея, почему мне это нужно?

0

Я думаю, что правильная вещь - это издеваться над службой. Вы захотите проверить различные случаи, которые могут быть возвращаемым значением, и что правильное значение передается методу службы.

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

    ... 
} 

или

@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 
}