Skip to content

Commit

Permalink
Allow using the setup method in Spock to setup data via a separate tr…
Browse files Browse the repository at this point in the history
…ansaction
  • Loading branch information
graemerocher committed Nov 30, 2015
1 parent 20bcb65 commit cc2f2ed
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
Expand Up @@ -56,6 +56,9 @@ class GrailsTransactionTemplate {

GrailsTransactionTemplate(PlatformTransactionManager transactionManager, GrailsTransactionAttribute transactionAttribute) {
this.transactionAttribute = transactionAttribute;
if(transactionManager == null) {
throw new IllegalStateException("No transactionManager was specified. Using @Transactional or @Rollback requires a valid configured transaction manager. If you are running in a unit test ensure the test has been properly configured and that you run the test suite not an individual test method.")
}
this.transactionTemplate = new TransactionTemplate(transactionManager, this.transactionAttribute)
}

Expand Down
Expand Up @@ -22,6 +22,7 @@ import groovy.transform.TypeChecked
import org.codehaus.groovy.ast.stmt.Statement
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.transaction.annotation.Propagation

import static org.grails.compiler.injection.GrailsASTUtils.*
import grails.transaction.NotTransactional
Expand Down Expand Up @@ -72,6 +73,7 @@ class TransactionalTransform implements ASTTransformation{
private static final String METHOD_EXECUTE = "execute"
private static final Set<String> METHOD_NAME_EXCLUDES = new HashSet<String>(Arrays.asList("afterPropertiesSet", "destroy"));
private static final Set<String> ANNOTATION_NAME_EXCLUDES = new HashSet<String>(Arrays.asList(PostConstruct.class.getName(), PreDestroy.class.getName(), Transactional.class.getName(), Rollback.class.getName(), "grails.web.controllers.ControllerMethod", NotTransactional.class.getName()));
private static final String SPEC_CLASS = "spock.lang.Specification";
public static final String PROPERTY_DATA_SOURCE = "datasource"

@Override
Expand Down Expand Up @@ -127,9 +129,18 @@ class TransactionalTransform implements ASTTransformation{
if(hasAnnotation(md, DelegatingMethod.class)) continue
weaveTransactionalMethod(source, classNode, annotationNode, md);
}
else if("setup".equals(methodName) && isSpockTest(classNode)) {
def requiresNewTransaction = new AnnotationNode(annotationNode.classNode)
requiresNewTransaction.addMember("propagation", new PropertyExpression(new ClassExpression(ClassHelper.make(Propagation.class)), "REQUIRES_NEW"))
weaveTransactionalMethod(source, classNode, requiresNewTransaction, md, "execute")
}
}
}

public static boolean isSpockTest(ClassNode classNode) {
return GrailsASTUtils.isSubclassOf(classNode, SPEC_CLASS);
}

private boolean hasExcludedAnnotation(MethodNode md) {
boolean excludedAnnotation = false;
for (AnnotationNode annotation : md.getAnnotations()) {
Expand All @@ -151,7 +162,7 @@ class TransactionalTransform implements ASTTransformation{
}
}

protected void weaveTransactionalMethod(SourceUnit source, ClassNode classNode, AnnotationNode annotationNode, MethodNode methodNode) {
protected void weaveTransactionalMethod(SourceUnit source, ClassNode classNode, AnnotationNode annotationNode, MethodNode methodNode, String executeMethodName = getTransactionTemplateMethodName()) {
if(isApplied(methodNode, this.getClass())) {
return
}
Expand Down Expand Up @@ -197,9 +208,9 @@ class TransactionalTransform implements ASTTransformation{

final methodArgs = new ArgumentListExpression()
methodArgs.addExpression(callCallExpression)
final executeMethodCallExpression = new MethodCallExpression(transactionTemplateVar, getTransactionTemplateMethodName(), methodArgs)
final executeMethodCallExpression = new MethodCallExpression(transactionTemplateVar, executeMethodName, methodArgs)
final executeMethodParameters = [new Parameter(ClassHelper.make(Closure), null)] as Parameter[]
final executeMethodNode = transactionTemplateClassNode.getMethod(getTransactionTemplateMethodName(), executeMethodParameters)
final executeMethodNode = transactionTemplateClassNode.getMethod(executeMethodName, executeMethodParameters)
executeMethodCallExpression.setMethodTarget(executeMethodNode)

if(methodNode.getReturnType() != ClassHelper.VOID_TYPE) {
Expand Down
Expand Up @@ -42,6 +42,40 @@ import spock.lang.Specification
/**
*/
class TransactionalTransformSpec extends Specification {

void "Test @Rollback when applied to Spock specifications"() {
when:"A new instance of a class with a @Transactional method is created that subclasses another transactional class"
Class mySpec = new GroovyShell().evaluate('''
import grails.transaction.*
import grails.transaction.TransactionManagerAware
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.annotation.Isolation
import org.springframework.transaction.annotation.Propagation
import spock.lang.Specification
@Rollback
class MySpec extends Specification {
def setup() {
}
void "my test method"() {
expect:
1 == 1
}
}
MySpec
''')

then:"It implements TransactionManagerAware"
TransactionManagerAware.isAssignableFrom(mySpec)
mySpec.getDeclaredMethod('setup')
mySpec.getDeclaredMethod('$tt__setup', TransactionStatus)
mySpec.getDeclaredMethod('$spock_feature_0_0')
mySpec.getDeclaredMethod('$tt__$spock_feature_0_0', TransactionStatus)
}

@Issue('#701')
void "Test @Transactional with a datasource specified isn't TransactionManager aware, but has appropriate autowired and qualifier"() {
when:"A new instance of a class with a @Transactional method is created that subclasses another transactional class"
Expand Down

0 comments on commit cc2f2ed

Please sign in to comment.