2010-01-15 3 views
1

Я пытаюсь получить транзакции, работающие в сервисе Grails, но я не получаю ожидаемых результатов. Может ли кто-нибудь сказать мне, что я делаю что-то неправильно, если мои предположения не работают?Сервисные операции Grails

Мой класс домен:

class Account { 

    static constraints = { 
    balance(min: 0.00) 
    } 

    String companyName 
    BigDecimal balance = 0.00 
    Boolean active = true 

    String toString() { 
    return "${companyName} : ${balance}" 
    } 
} 

Моя служба:

class AccountService { 

    static transactional = true 

    def transfer(Account source, Account destination, amount) throws RuntimeException { 

    if (source.active && destination.active) { 
     source.balance -= amount 

     if (!source.save(flush: true)) { 
     throw new RuntimeException("Could not save source account.") 
     } else { 
     destination.balance += amount 

     if (!destination.save(flush: true)) { 
      throw new RuntimeException("Could not save destination account.") 
     } 
     } 
    } else { 
     throw new RuntimeException("Both accounts must be active.") 
    } 
    } 

    def someMethod(Account account) throws RuntimeException { 

    account.balance = -10.00 

    println "validated: ${account.validate()}" 

    if(!account.validate()) { 
     throw new RuntimeException("Rollback!") 
    } 
    } 
} 

Мой блок тест:. импорт grails.test *

class AccountServiceTests extends GrailsUnitTestCase { 

    def AccountService 

    protected void setUp() { 
    super.setUp() 
    mockDomain(Account) 
    AccountService = new AccountService() 
    } 

    protected void tearDown() { 
    super.tearDown() 
    } 

    void testTransactional() { 
    def account = new Account(companyName: "ACME Toy Company", balance: 2000.00, active: true) 

    def exception = null 

    try { 
     AccountService.someMethod(account) 
    } catch (RuntimeException e) { 
     exception = e 
    } 

    assert exception instanceof RuntimeException 

    println "exception thrown: ${exception.getMessage()}" 

    assertEquals 2000.00, account.balance 
    } 
} 

Результат:

Testsuite: AccountServiceTests 
Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 1.068 sec 
------------- Standard Output --------------- 
--Output from testTransactional-- 
validated: false 
exception thrown: Rollback! 
------------- ---------------- --------------- 
------------- Standard Error ----------------- 
--Output from testTransactional-- 
------------- ---------------- --------------- 

Testcase: testTransactional took 1.066 sec 
    FAILED 
expected:<2000.00> but was:<-10.00> 
junit.framework.AssertionFailedError: expected:<2000.00> but was:<-10.00> 
    at AccountServiceTests.testTransactional(AccountServiceTests.groovy:89) 
    at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:203) 
    at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy) 
    at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:147) 
    at _GrailsTest_groovy$_run_closure1_closure19.doCall(_GrailsTest_groovy:113) 
    at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:96) 
    at TestApp$_run_closure1.doCall(TestApp.groovy:66) 
    at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324) 
    at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334) 
    at gant.Gant$_dispatch_closure6.doCall(Gant.groovy) 
    at gant.Gant.withBuildListeners(Gant.groovy:344) 
    at gant.Gant.this$2$withBuildListeners(Gant.groovy) 
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source) 
    at gant.Gant.dispatch(Gant.groovy:334) 
    at gant.Gant.this$2$dispatch(Gant.groovy) 
    at gant.Gant.invokeMethod(Gant.groovy) 
    at gant.Gant.processTargets(Gant.groovy:495) 
    at gant.Gant.processTargets(Gant.groovy:480) 

Мои ожидания:

Когда счет дается отрицательный баланс, он не должен подтверждать (что не делает), RuntimeException должен быть выброшен (который он есть), и учетная запись должна откат к предыдущему состоянию (баланс: 2000), где он разваливается.

Что мне здесь не хватает?

ответ

4

Модульные тесты - это только классы Groovy или Java, поэтому нет контекста приложения Spring и, следовательно, нет поддержки транзакций. Вы должны были бы издеваться над ним за единичный тест, но это не будет проверять транзакцию, а только качество макетов. Конвертируйте тесту интеграции и не требует нового на службе, использовать инъекции зависимостей:

class AccountServiceTests extends GroovyTestCase { 

    def AccountService 

    void testTransactional() { 
    def account = new Account(companyName: "ACME Toy Company", balance: 2000.00, 
           active: true) 
    account.save() 
    assertFalse account.hasErrors() 

    String message = shouldFail(RuntimeException) { 
     AccountService.someMethod(account) 
    } 

    println "exception thrown: $message" 

    assertEquals 2000.00, account.balance 
    } 
} 

Обратите внимание, что фактическое исключение может быть исключением обертки с брошенным исключением в качестве его причины.

+0

Эй, Берт, имеет смысл. Увы, до сих пор не повезло. Он по-прежнему бросает исключение, как должно, но он ничего не скатывает. – Thody

+2

Вот как это работает. Данные экземпляра учетной записи не будут отменены, но это изменение не будет сохранено. Добавьте «def sessionFactory» в поле ввода с зависимостями и добавьте вызов «sessionFactory.currentSession.clear()» и «account = Account.get (account.id)» перед последним assertEquals, чтобы принудительно перезагрузить учетную запись экземпляр, и тест пройдет. –

+1

Простое примечание для людей, использующих это для написания интеграционных тестов для транзакционных сервисов. Интеграционные тесты сами по себе являются транзакционными, поэтому ваша тестируемая служба не будет в новой транзакции. Это может привести к неожиданному поведению.Обходной путь заключается в отключении транзакций в вашем интеграционном тесте (static transactional = false). –

0

Какая версия Grails вы используете? v1.1.1 имеет ошибку, в которой transactional = true не работает должным образом.

+0

Запуск v1.1.1. – Thody

+1

Да. Попробуйте вывести 'transactional = true'. Посмотрите, есть ли у вас лучшие результаты. Или попробуйте с 1.2. –

+0

http://jira.codehaus.org/browse/GRAILS-4644 –

1

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

Согласно Jira GRAILS-3765 это известная проблема и все еще открыта. (Я не уверен, почему он говорит только «влияет на версию 1.0.4», когда 1.1.x отсутствовал в течение длительного времени).

Основываясь на этих моментах, я думаю, что это ошибка в Grails. Замечание Джиры обходное решение, но я не пробовал. В соответствии с этой проблемой, однако, она будет работать при запуске приложения; это можно подтвердить вручную.

Смежные вопросы