Grails服务事务 [英] Grails Service Transactions
问题描述
我试图让事务在Grails服务中工作,但我没有得到我期待的结果。有人告诉我,如果我做错了什么,如果我的假设是关闭的?
我的域类:
class Account {
static constraints = {
balance(min:0.00)
}
字符串companyName
BigDecimal余额= 0.00
布尔值active = true
字符串toString(){
return$ {companyName}:$ {余额}
}
我的服务:
class AccountService {
static transactional = true
def传输(帐户来源,帐户目的地,金额)引发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)){
抛出新的RuntimeException(无法保存目标帐户。)
}
}
} else {
throw new RuntimeException(两个帐户必须处于活动状态。)
}
}
def someMethod(帐户帐户)引发RuntimeException {
account.balance = -10.00
$ b printlnvalidated:$ {account.validate()}
if(!account.validate()){
throw new RuntimeException(回滚!)
}
}
}
我的单元测试: import grails.test。*
class AccountServiceTests extends GrailsUnitTestCase {
帐户服务
保护无效setUp(){
super.setUp()
mockDomain(帐户)
AccountService = new AccountService()
保护无效tearDown(){
super.tearDown()
}
void testTransact ional(){
def account = new Account(companyName:ACME Toy Company,余额:2000.00,active:true)
$ b $ def例外=空
尝试{
AccountService.someMethod(account)
} catch(RuntimeException e){
exception = e
}
assert exception instanceof RuntimeException
println异常抛出:$ {exception.getMessage()}
assertEquals 2000.00,account.balance
}
}
结果:
Testsuite:AccountServiceTests
测试运行:1,失败:1,错误:0,流逝时间:1.068秒
-------------标准输出---------------
- 从testTransactional输出 -
验证:false
抛出异常:回滚!
------------- ---------------- ---------------
-------------标准错误-----------------
- 从testTransactional输出 -
- ------------ ---------------- ---------------
Testcase:testTransactional花了1.066秒
失败
预期值:< 2000.00>但是:< -10.00>
junit.framework.AssertionFailedError:expected:< 2000.00>但是:< -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 $ $ _ $ _ 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(_GrailsTest_groovy: .gov。: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 (Gant.groovy)
at gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:495)
at gant.Gant.processTargets(Gant.groovy: 480)
我的期望:
当帐户被赋予一个负余额时,它不应该被验证(它不会),应该抛出一个RuntimeException(它是),并且该帐户应该回滚到它以前的状态(余额:2000),这是它分崩离析的地方。
我在这里错过了什么?
单元测试只是Groovy或Java类,所以没有Spring应用程序上下文,因此没有事务支持。你不得不嘲笑单元测试,但这不会测试事务性,只是模拟的质量。将此转换为集成测试,并且不要在服务上调用new,请使用依赖注入:
类AccountServiceTests扩展GroovyTestCase {
帐户服务
无效testTransactional(){
def帐户=新帐户(公司名称:ACME玩具公司,余额:2000.00,
活动: true)
account.save()
assertFalse account.hasErrors()
String message = shouldFail(RuntimeException){
AccountService.someMethod(account)
$ b println异常抛出:$ message
assertEquals 2000.00,account.balance
}
}
$ b请注意,实际的异常可能是一个包装异常,因为您的抛出异常是其原因。
I'm trying to get transactions working within a Grails service, but I'm not getting the results I'm expecting. Can someone tell me if I'm doing something wrong, if my assumptions are off?
My domain class:
class Account { static constraints = { balance(min: 0.00) } String companyName BigDecimal balance = 0.00 Boolean active = true String toString() { return "${companyName} : ${balance}" } }
My service:
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!") } } }
My unit test: import 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 } }
The result:
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)
My expectation:
When the account is given a negative balance, it shouldn't validate (which it doesn't), a RuntimeException should be thrown (which it is), and the account should rollback to it's previous state (balance: 2000), which is where it falls apart.
What am I missing here?
解决方案Unit tests are just Groovy or Java classes, so there's no Spring application context and hence no transaction support. You'd have to mock that for a unit test, but that wouldn't be testing transactionality, just the quality of the mocks. Convert this to an integration test and don't call new on the service, use dependency injection:
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 } }
Note that the actual exception may be a wrapper exception with your thrown exception as its cause.
这篇关于Grails服务事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!