Imagine you have some kind of simple code where you insert two rows into a database within one transaction.
How can you make the second one fail in a test in order to check that the first one gets rolled back?
Would it be a webservice, I would mock it through meta-programming in order to let the second call fail.
But with database code I am unsure. Is there maybe some kind of jdbc driver which throws exceptions for (let's say) every 7th operation?
ok, let's be more specific:
Imagine you have some legacy code. It is not well structured but you also don't want to refactore it yet (let's first write some tests!). There seems to be a problem with the transactional behaviour and you would like test your code.
Since the code basically works, it would be hard to make parts of it fail by violation constraints.
So what I would like to do in my tests is to simulate what happens if the database fails (maybe because there is a deadlock, unavailability, disk full or something like that) or when you "pull the plug".
Integration test would be an appropriate place to test transactions and rollbacks. Something like this sample should suffice:
package com.example
import grails.test.mixin.integration.Integration
import grails.transaction.*
import spock.lang.*
import org.springframework.beans.factory.annotation.Autowired
@Integration
@Rollback
class TransactionIntSpec extends Specification {
@Autowired
FooService service
void "test transaction rollback"() {
given: 'bar service throws exception'
service = Mock(FooService) {
serviceBar() >> { throw new Exception() }
}
when:
service.serviceMethod()
then:
!Foo.count() && !Bar.count()
}
void "test transaction successfull"() {
when:
service.serviceMethod()
then:
Foo.count() && Bar.count()
}
}
where Foo
and Bar
are just simple domain classes and the service class would look like:
package com.example
import grails.transaction.Transactional
@Transactional
class FooService {
def serviceMethod() {
serviceFoo()
serviceBar()
}
def serviceFoo() {
new Foo().save(flush: true, failOnError: true)
}
def serviceBar() {
new Bar().save(flush: true, failOnError: true)
}
}
Tested in Grails 3.0.2