Rollback de transação em testes de integracão
10/12/2015 16:29
1
Olá Pessoal.

Estou com o seguinte caso.

Tenho um service que faz a persistência de uma classe pai e seus filhos.

Em caso de erro, retorno um erro que extende de RuntimeException para o controller e o service realiza o rollback das transações.

Em produção está tudo funcionando conforme esperado, mas nos meus testes de integração, devido ao fato da rollback ocorrer somente ao fim do teste, o registro pai ainda existe ao realizar o assert. 

Entendo que esse é um comportamento esperado pelo fato que o teste é Transactional = true e quando marco o transactional como false, o comportamento passa a ser igual ao do funcionamento em produção, porém o banco deixa de ser limpo após cada teste ser executado.

A pergunta: Existe alguma maneira de manter o service como transactional = true e ainda assim, fazer com que o Rollback do service ocorra no momento que eu lanço a exception?

Se não fui claro, me avisem que tento implementar um exemplo.

Abraço e obrigado
Tags: Grails, integration test, transactional, rollback


0
Oi Rafael,

há um aspecto das transações que você deve levar em consideração e que muitas vezes é ignorado: por padrão, você está usando transações declarativas (que é o padrão no Java EE). Com isto, o rollback passa a ser tratado pelo container, e não pelo programador. E o rollback só ocorre se a exceção disparada for derivada de RuntimeException. Lembre-se disto: exceções que derivam de Exception não executam o rollback do seu código.

Se quiser, você pode também desabilitar o rollback automático do próprio teste. Se estiver usando o antigo framework de testes do Grails (pré 2.3), basta incluir o atributo estático transactional com valor igual a false.
Se quiser obter o mesmo resultado com Grails 3, remova a anotação @Rollback da sua spec


0
Olá Kico. Muito obrigado pela resposta.

Quando as pontos, estou lançando uma RuntimeException para que o rollback aconteça, tanto que em tempo de desenvolvimento e produção tudo funciona.

O que me incomoda é o fato de que no teste de integração, o Rollback ainda não aconteceu quando o Assert é realizado, desta forma, se eu verificar o banco de dados o resultado está lá, uma vez que nos testes o rollback só acontece ao término da execução.

Gostaria de uma solução que eu não fosse preciso utilizar o Transactional = false, pois desta forma, os registros salvos por outros testes permanecem no banco de dados mesmo sendo create-drop.

Talvez tenha como forçar que a transação realizada no service seja separada da transação do teste de integração. Isso é possível?

Segue exemplo do código para que possa entender melhor:
Meu Teste:

?class MyControllerIntegrationSpec extends IntegrationSpec {
?void "Test save father but fail on childrenSuccess"() {
? ?given: 'Load MyController'
  def c = new MyController()
  def json = loadJSONData() //fatherName = "Paulo", children = null
  and: 'with request set'
  c.request.contentType = "application/json"
  c.request.method = 'POST'
  c.request.json = json
? ?c.save()
? ?assertEquals("fail", ?ac.response.json.message) // Assert verdadeiro
  assertTrue(Father.findByFatherName("Paulo") == null) // Falha pois existe o registro no banco de dados ainda
}


Meu Controller: 
class MyController {
?def myService
?def save() {
? try {
? ? ?myService.save(request.JSON)
? ? ?def result = ["message":"success"]
? ? ?render result as JSON 
? ?} ?catch( Exception ex) {
? ? def result = ["message":"fail"]
? ? ?render result as JSON 
?}
}


Meu Service

@Transactionalclass MyService {
?def save(def json) {
?new Father(json.fatherName).save(flush:true, failOnError: true) // salva com sucesso
 new Children(json.children).save(flush:true, failOnError: true) // Falha pois o children é null
?} ?
}
11/12/2015 14:36


0
Desculpe mas apareceu um monte de caracter "?". Por favor desconsiderem esses caracteres. 
11/12/2015 14:37


0
Ao invés de testar a presença do registro, teste se a exceção foi lançada. O resto é com o framework


0
Olá Magno.
Obrigado pela resposta. No teste do service estou validando se a exceção foi lançada.

Segue abaixo a solução que utilizei para esse teste de integração. Note que como o transactional agora é false, estou recriando o Datasource a cada teste como ocorre no Transacrional = true, porém tenho que fazer isso manualmente.

import groovy.sql.Sql
class MyControllerIntegrationSpec extends IntegrationSpec {
static transactional = false
?def sessionFactory
?def dataSource
?def grailsApplication
?def schemaDump
?def sql
?def setup() {
     sql = new Sql(dataSource)
        schemaDump = File.createTempFile("test-database-dump", ".sql") // Java 7 API
        sql.execute("script drop to ${schemaDump.absolutePath}")
?}
?def cleanup() {
? ?sql.execute("runscript from ${schemaDump.absolutePath}")
    ?def sf = grailsApplication.getMainContext().getBean('sessionFactory')
    ?sf.getCurrentSession().clear()
}?
}

?
Não ficou a melhor solução do mundo, mas está funcionando muito bem.
Obrigado
12/12/2015 18:49



Ainda não faz parte da comunidade???

Para se registrar, clique aqui.


Aprenda Groovy e Grails com a Formação itexto!

Newsletter Semana Groovy

Assinar

Envie seu link!


Livro de Grails


/dev/All

Os melhores blogs de TI (e em português) em um único lugar!

 
Creative Commons
RSS Grails Brasil é mantido por itexto Consultoria.
Em caso de problemas contacte Henrique Lobo Weissmann (Kico) por e-mail: kico@itexto.com.br
Todo o conteúdo presente neste site adota o Creative Commons como licença padrão.
Ver: 4.14.0
itexto