I am attempting to delete and create data using GORM, in the Background phase of my cucumber tests. When I attempt to delete an object in the Background that was changed in the previous scenario, I get what looks like an optimistic locking violation. I am using grails 2.3.8, and hibernate 3.6.10.13, and cucumber 0.10.0. I get the same error with either mysql or the H2 database.
This is the error message:
| Error 2014-05-02 14:24:09,092 [main] ERROR events.PatchedDefaultFlushEventListener - Could not synchronize database state with session
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.example.angugrails.auth.User#170]
Line | Method
->> 13 | doCall in steps.DataSteps$_run_closure1_closure3
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 11 | doCall in steps.DataSteps$_run_closure1
| 146 | invoke . . . in cucumber.runtime.groovy.GroovyBackend
| 67 | call in cucumber.runtime.groovy.GroovyStepDefinition$1
| 12 | timeout . . . in cucumber.runtime.Timeout
| 64 | execute in cucumber.runtime.groovy.GroovyStepDefinition
| 38 | runStep . . . in cucumber.runtime.StepDefinitionMatch
| 289 | runStep in cucumber.runtime.Runtime
| 44 | runStep . . . in cucumber.runtime.model.StepContainer
| 39 | runSteps in ''
| 49 | runBackground in cucumber.runtime.model.CucumberScenario
| 38 | run in ''
| 116 | run . . . . . in cucumber.runtime.model.CucumberFeature
| 59 | doCall in grails.plugin.cucumber.Cucumber$_run_closure2
| 58 | run . . . . . in grails.plugin.cucumber.Cucumber
| 121 | runFeatures in grails.plugin.cucumber.CucumberTestType
| 57 | doRun . . . . in ''
| 102 | doCall in _GrailsTest_groovy$_run_closure1
| 32 | doCall . . . in TestApp$_run_closure1
^ 120 | main in com.intellij.rt.execution.application.AppMain
This is the hibernate config from DataSource.groovy, I am attempting to disable the second_level_cache because perhaps cacheing objects retrieved in the cucumber test could cause this problem:
hibernate {
cache.use_second_level_cache = false
cache.use_query_cache = false
cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory' // Hibernate 3
// cache.region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory' // Hibernate 4
singleSession = true // configure OSIV singleSession mode
}
The Background of the feature file looks like this:
Feature: User Profile
Background:
Given the db is reset
Given the db is loaded with new user. username: "user1" email: "testuser1@example.com"
password: "testpassword1"
The database cucumber step functions look like this:
Given(~'^the db is reset') { ->
User.findAll().each {
it.delete(flush: true)
}
}
When(~'^the db is loaded with new user. username: "([^"]*)" email: "([^"]*)" password: "([^"]*)"$') {
String username, String email, String password ->
def user = new User(username: username, email: email, password: password, enabled: true, accountLocked: false)
if (!user.validate()) {
throw new Exception("unexpected error.")
} else {
user.save(flush: true)
def debug = User.findByUsername("user1")
debug.username
}
}
The scenario that I'm running uses webdriver and the web interface to change the user's password. Once that scenario is run, the Background will fail with the above error for all subsequent scenarios in the feature.
All my tests that do not change any user attributes succeed. How do I avoid this optimistic locking error?
Thanks!
You should not have queries, in particular writing queries in steps. Move that code into a service to get proper transaction/commit handling.
This is true for normal Grails too but, in my experience, it doesn't show up so directly.
I like to call that cleanup from a @Before hook. That keeps that technical stuff like 'the db is reset' out of the scenarios.
Note that directly calling grails stuff from steps is deprecated for new code since grails 2.3. Current style will still work in grails non forked mode but it will never work in forked mode. See this article.