You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ma...@apache.org on 2026/08/04 12:55:25 UTC

(grails-core) 01/01: fix: improve type hints in web unit test support classes

This is an automated email from the ASF dual-hosted git repository.

matrei pushed a commit to branch test-support-type-hints
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 4bba1943d880a3f4de7be561b55a189d916fac7e
Author: Mattias Reichel <ma...@gmail.com>
AuthorDate: Tue Aug 4 14:52:59 2026 +0200

    fix: improve type hints in web unit test support classes
    
    And some readability cleanup.
---
 .../test/mixin/ControllerUnitTestMixinTests.groovy | 141 ++++++++++++---------
 .../test/mixin/InterceptorUnitTestMixinSpec.groovy |  35 +++--
 .../servlet/mvc/TagLibDynamicMethodsTests.groovy   |  14 +-
 .../testing/ParameterizedGrailsUnitTest.groovy     |  20 +--
 grails-testing-support-web/build.gradle            |   2 +
 .../grails/testing/web/GrailsWebUnitTest.groovy    |  70 +++++-----
 .../grails/testing/web/UrlMappingsUnitTest.groovy  | 101 ++++++++-------
 .../web/controllers/ControllerUnitTest.groovy      |  22 ++--
 .../web/interceptor/InterceptorUnitTest.groovy     |  49 ++++---
 .../testing/web/taglib/TagLibUnitTest.groovy       |  44 ++++---
 10 files changed, 276 insertions(+), 222 deletions(-)

diff --git a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy
index 67176f35ef..666fc0889e 100644
--- a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy
+++ b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/ControllerUnitTestMixinTests.groovy
@@ -16,9 +16,17 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
 package grails.test.mixin
 
+import groovy.transform.CompileStatic
+
+import jakarta.servlet.http.HttpServletResponse
+
+import spock.lang.Specification
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.MessageSource
+
 import grails.artefact.Artefact
 import grails.converters.JSON
 import grails.converters.XML
@@ -29,22 +37,16 @@ import grails.web.mime.MimeUtility
 import org.grails.plugins.testing.GrailsMockMultipartFile
 import org.grails.validation.ConstraintEvalUtils
 import org.grails.web.servlet.mvc.SynchronizerTokensHolder
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.context.MessageSource
-import org.springframework.web.multipart.MultipartFile
-import spock.lang.Specification
-
-import jakarta.servlet.http.HttpServletResponse
 
 /**
  * Specification for the behavior of the ControllerUnitTestMixin
- *
- * @author Graeme Rocher
  */
 class ControllerUnitTestMixinTests extends Specification implements ControllerUnitTest<TestController> {
 
     // Cache the static field helper interface for performance
-    private static final Class<?> STATIC_FIELD_HELPER = Class.forName('grails.validation.Validateable$Trait$StaticFieldHelper')
+    private static final Class<?> STATIC_FIELD_HELPER = Class.forName(
+            'grails.validation.Validateable$Trait$StaticFieldHelper'
+    )
 
     def setup() {
         ConstraintEvalUtils.clearDefaultConstraints()
@@ -68,12 +70,18 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         }
     }
 
+    @CompileStatic
+    void "mockController returns the typed controller"() {
+        expect: "no compilation error"
+        TypedTestController c = mockController(TypedTestController)
+    }
+
     void testRenderText() {
         when:
         controller.renderText()
         
         then:
-        response.text == "good"
+        response.text == 'good'
     }
 
     void testCallingSuperMethod() {
@@ -99,7 +107,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         controller.renderView()
 
         then:
-        "/test/foo" == view
+        '/test/foo' == view
     }
 
     void testRenderXml() {
@@ -108,7 +116,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         then:
         "<book title='Great'/>" == controller.response.contentAsString
-        "Great" == controller.response.xml.@title.text()
+        'Great' == controller.response.xml.@title.text()
     }
 
     void testRenderJson() {
@@ -117,7 +125,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         then:
         '{"book":"Great"}' == controller.response.contentAsString
-        "Great" == controller.response.json.book
+        'Great' == controller.response.json.book
     }
 
     void testRenderAsJson() {
@@ -126,13 +134,13 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         then:
         '{"foo":"bar"}' == controller.response.contentAsString
-        "bar" == controller.response.json.foo
+        'bar' == controller.response.json.foo
     }
 
     void testRenderState() {
         when:
-        params.foo = "bar"
-        request.bar = "foo"
+        params.foo = 'bar'
+        request.bar = 'foo'
         
         controller.renderState()
 
@@ -155,7 +163,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
     }
 
     void testControllerAutowiring() {
-        messageSource.addMessage("foo.bar", request.locale, "Hello World")
+        messageSource.addMessage('foo.bar', request.locale, 'Hello World')
 
         
         when:
@@ -215,7 +223,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
     void testFileUpload() {
         when:
-        final file = new GrailsMockMultipartFile("myFile", "foo".bytes)
+        def file = new GrailsMockMultipartFile('myFile', 'foo'.bytes)
         request.addFile(file)
         controller.uploadFile()
 
@@ -232,7 +240,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         controller.renderTemplate(templateName)
 
         then:
-        response.contentAsString == "Hello 10"
+        response.contentAsString == 'Hello 10'
     }
 
     void testRenderBasicTemplateWithTags() {
@@ -240,12 +248,12 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         def templateName = 'testRenderBasicTemplateWithTags'
 
         when:
-        messageSource.addMessage("foo.bar", request.locale, "World")
+        messageSource.addMessage('foo.bar', request.locale, 'World')
         groovyPages["/test/_${templateName}.gsp" as String] = 'Hello <g:message code="foo.bar" />'
         controller.renderTemplate(templateName)
 
         then:
-        response.contentAsString == "Hello World"
+        response.contentAsString == 'Hello World'
     }
 
     void testRenderBasicTemplateWithLinkTag() {
@@ -257,7 +265,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         controller.renderTemplate(templateName)
 
         then:
-        response.contentAsString == "Hello /bar"
+        response.contentAsString == 'Hello /bar'
     }
 
     void testInvokeTagLibraryMethod() {
@@ -265,7 +273,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         controller.renderTemplateContents()
 
         then:
-        response.contentAsString == "/foo"
+        response.contentAsString == '/foo'
     }
 
     void testInvokeTagLibraryMethodViaNamespace() {
@@ -275,7 +283,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
         controller.renderTemplateContentsViaNamespace()
 
         then:
-        response.contentAsString == "Hello World"
+        response.contentAsString == 'Hello World'
     }
 
     void testInvokeWithCommandObject() {
@@ -292,7 +300,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        cmd.name = "Bob"
+        cmd.name = 'Bob'
         cmd.clearErrors()
         cmd.validate()
         controller.handleCommand(cmd)
@@ -311,7 +319,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "POST"
+        request.method = 'POST'
         controller.action1()
         
         then:
@@ -320,7 +328,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "PUT"
+        request.method = 'PUT'
         controller.action1()
         
         then:
@@ -329,7 +337,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "PATCH"
+        request.method = 'PATCH'
         controller.action1()
         
         then:
@@ -338,7 +346,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "DELETE"
+        request.method = 'DELETE'
         controller.action1()
         
         then:
@@ -440,7 +448,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "POST"
+        request.method = 'POST'
         controller.method1()
 
         then:
@@ -449,7 +457,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "PUT"
+        request.method = 'PUT'
         controller.method1()
 
         then:
@@ -458,7 +466,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "PATCH"
+        request.method = 'PATCH'
         controller.method1()
 
         then:
@@ -467,7 +475,7 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 
         when:
         response.reset()
-        request.method = "DELETE"
+        request.method = 'DELETE'
         controller.method1()
 
         then:
@@ -584,7 +592,12 @@ class ControllerUnitTestMixinTests extends Specification implements ControllerUn
 @Controller
 class TestController  {
 
-    static allowedMethods = [action2: 'POST', action3: ['POST', 'PUT', 'PATCH'], method2: 'POST', method3: ['POST', 'PUT', 'PATCH']]
+    static allowedMethods = [
+            action2: 'POST',
+            action3: ['POST', 'PUT', 'PATCH'],
+            method2: 'POST',
+            method3: ['POST', 'PUT', 'PATCH']
+    ]
 
     def action1 = {
         render 'action 1'
@@ -610,41 +623,41 @@ class TestController  {
         render 'method 3'
     }
 
-    def handleCommand( TestCommand test ) {
+    def handleCommand(TestCommand test) {
         if (test.hasErrors()) {
-            render "Bad"
+            render 'Bad'
         }
         else {
-            render "Good"
+            render 'Good'
         }
     }
 
     def uploadFile = {
         assert request.method == 'POST'
-        assert request.contentType == "multipart/form-data"
-        MultipartFile file = request.getFile("myFile")
-        file.transferTo(new File("/local/disk/myFile"))
+        assert request.contentType == 'multipart/form-data'
+        def file = request.getFile('myFile')
+        file.transferTo(new File('/local/disk/myFile'))
     }
 
     def renderTemplateContents = {
-        def contents = createLink(controller:"foo")
+        def contents = createLink(controller: 'foo')
         render contents
     }
     def renderTemplateContentsViaNamespace = {
-        def contents = g.render(template:"bar")
+        def contents = g.render(template: 'bar')
 
         render contents
     }
     def renderText = {
-        render "good"
+        render 'good'
     }
 
     def redirectToController = {
-        redirect(controller:"bar")
+        redirect(controller: 'bar')
     }
 
     def renderView = {
-        render(view:"foo")
+        render(view: 'foo')
     }
 
     def renderTemplate(String template) {
@@ -652,23 +665,23 @@ class TestController  {
     }
 
     def renderXml = {
-        render(contentType:"text/xml") {
-            book(title:"Great")
+        render(contentType: 'text/xml') {
+            book(title: 'Great')
         }
     }
 
     def renderJson = {
-        render(contentType:"text/json") {
-            book "Great"
+        render(contentType: 'text/json') {
+            book 'Great'
         }
     }
 
     def renderAsJson = {
-        render([foo:"bar"] as JSON)
+        render([foo: 'bar'] as JSON)
     }
 
     def renderWithFormat = {
-        def data = [foo:"bar"]
+        def data = [foo: 'bar']
         withFormat {
             xml { render data as XML }
             html data
@@ -676,7 +689,7 @@ class TestController  {
     }
 
     def renderWithRequestFormat = {
-        def data = [foo:"bar"]
+        def data = [foo: 'bar']
         request.withFormat {
             xml { render data as XML }
             html data
@@ -684,35 +697,35 @@ class TestController  {
     }
 
     def renderState = {
-        render(contentType:"text/xml") {
+        render(contentType: 'text/xml') {
             println params.foo
             println request.bar
             requestInfo {
                 for (p in params) {
-                    parameter(name:p.key, value:p.value)
+                    parameter(name: p.key, value: p.value)
                 }
                 request.each {
-                    attribute(name:it.key, value:it.value)
+                    attribute(name: it.key, value: it.value)
                 }
             }
         }
     }
 
-    MessageSource messageSource
     @Autowired
     MimeUtility mimeUtility
+    MessageSource messageSource
 
     def renderMessage() {
-        assert mimeUtility !=null
+        assert mimeUtility != null
         assert grailsLinkGenerator != null
-        render messageSource.getMessage("foo.bar", null, request.locale)
+        render messageSource.getMessage('foo.bar', null, request.locale)
     }
 
     def renderWithForm() {
         withForm {
-            render "Good"
+            render 'Good'
         }.invalidToken {
-            render "Bad"
+            render 'Bad'
         }
     }
 }
@@ -721,17 +734,19 @@ class TestCommand {
     String name
 
     static constraints = {
-        name blank:false
+        name blank: false
     }
 }
 
-@Artefact("Controller")
+@Artefact('Controller')
 class SubController extends TestController {
     def method1() {
         super.method1()
     }
 }
 
+class TypedTestController implements grails.artefact.Controller {}
+
 class SomeValidateableThing implements Validateable {
     String name
     String email
diff --git a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/InterceptorUnitTestMixinSpec.groovy b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/InterceptorUnitTestMixinSpec.groovy
index 9a68432855..717677f20b 100644
--- a/grails-test-suite-uber/src/test/groovy/grails/test/mixin/InterceptorUnitTestMixinSpec.groovy
+++ b/grails-test-suite-uber/src/test/groovy/grails/test/mixin/InterceptorUnitTestMixinSpec.groovy
@@ -18,44 +18,53 @@
  */
 package grails.test.mixin
 
+import groovy.transform.CompileStatic
+
+import spock.lang.Specification
+
+import grails.artefact.Controller
 import grails.artefact.Interceptor
 import grails.testing.web.interceptor.InterceptorUnitTest
-import spock.lang.Specification
 
-/**
- * Created by graemerocher on 02/09/15.
- */
 class InterceptorUnitTestMixinSpec extends Specification implements InterceptorUnitTest<TestInterceptor> {
 
+    @CompileStatic
+    void "mockInterceptor returns the typed interceptor"() {
+        expect: "no compilation error"
+        TypedTestInterceptor i = mockInterceptor(TypedTestInterceptor)
+    }
+
     void "Test interceptor matching"() {
-        when:"A request matches the interceptor"
+        when: "A request matches the interceptor"
         withRequest(controller:"foo", action:"bar")
 
-        then:"The interceptor does match"
+        then: "The interceptor does match"
         interceptor.doesMatch()
 
-        when:"A request matches the interceptor"
+        when: "A request matches the interceptor"
         withRequest(controller:"foo", action:"not")
 
-        then:"The interceptor does match"
+        then: "The interceptor does match"
         !interceptor.doesMatch()
 
-        when:"A request matches the interceptor"
+        when: "A request matches the interceptor"
         withRequest(controller:"foo")
 
-        then:"The interceptor does match"
+        then: "The interceptor does match"
         !interceptor.doesMatch()
 
-        when:"A request matches the interceptor"
+        when: "A request matches the interceptor"
         withRequest(controller:"bar", action:"not")
 
-        then:"The interceptor does match"
+        then: "The interceptor does match"
         !interceptor.doesMatch()
     }
 }
 
 class TestInterceptor implements Interceptor {
     TestInterceptor() {
-        match(controller:"foo", action:"bar")
+        match(controller: 'foo', action: 'bar')
     }
 }
+
+class TypedTestInterceptor implements Interceptor {}
diff --git a/grails-test-suite-uber/src/test/groovy/org/grails/web/servlet/mvc/TagLibDynamicMethodsTests.groovy b/grails-test-suite-uber/src/test/groovy/org/grails/web/servlet/mvc/TagLibDynamicMethodsTests.groovy
index eb4b507895..55ad0a8057 100644
--- a/grails-test-suite-uber/src/test/groovy/org/grails/web/servlet/mvc/TagLibDynamicMethodsTests.groovy
+++ b/grails-test-suite-uber/src/test/groovy/org/grails/web/servlet/mvc/TagLibDynamicMethodsTests.groovy
@@ -18,12 +18,20 @@
  */
 package org.grails.web.servlet.mvc
 
+import groovy.transform.CompileStatic
+
 import grails.artefact.Artefact
 import grails.testing.web.taglib.TagLibUnitTest
 import spock.lang.Specification
 
 class TagLibDynamicMethodsTests extends Specification implements TagLibUnitTest<TestTagLib> {
 
+    @CompileStatic
+    void "mockTagLib returns the typed taglib instance"() {
+        expect: "no compilation error"
+        TestTagLib t = mockTagLib(TestTagLib)
+    }
+
     void testFlashObject() {
         when:
         tagLib.flash.test = "hello"
@@ -56,7 +64,6 @@ class TagLibDynamicMethodsTests extends Specification implements TagLibUnitTest<
     void testRequestObjects() {
         expect:
         tagLib.request != null
-
         tagLib.response != null
         tagLib.servletContext != null
     }
@@ -64,6 +71,5 @@ class TagLibDynamicMethodsTests extends Specification implements TagLibUnitTest<
 
 @Artefact("TagLibrary")
 class TestTagLib {
-    def myTag = {attrs, body -> body() }
- }
-
+    def myTag = { attrs, body -> body() }
+}
diff --git a/grails-testing-support-core/src/main/groovy/org/grails/testing/ParameterizedGrailsUnitTest.groovy b/grails-testing-support-core/src/main/groovy/org/grails/testing/ParameterizedGrailsUnitTest.groovy
index 60e1e7e8ee..e10208ab07 100755
--- a/grails-testing-support-core/src/main/groovy/org/grails/testing/ParameterizedGrailsUnitTest.groovy
+++ b/grails-testing-support-core/src/main/groovy/org/grails/testing/ParameterizedGrailsUnitTest.groovy
@@ -30,13 +30,13 @@ trait ParameterizedGrailsUnitTest<T> extends GrailsUnitTest {
     private T _artefactInstance
 
     Class<T> getTypeUnderTest() {
-        ParameterizedType parameterizedType = (ParameterizedType) getClass().genericInterfaces.find { genericInterface ->
+        def parameterizedType = getClass().genericInterfaces.find { genericInterface ->
             genericInterface instanceof ParameterizedType &&
-              ParameterizedGrailsUnitTest.isAssignableFrom((Class)((ParameterizedType)genericInterface).rawType)
-        }
+              ParameterizedGrailsUnitTest.isAssignableFrom((Class) ((ParameterizedType) genericInterface).rawType)
+        } as ParameterizedType
 
         if (parameterizedType?.actualTypeArguments != null) {
-            parameterizedType.actualTypeArguments[0]
+            parameterizedType.actualTypeArguments[0] as Class<T>
         } else {
             null
         }
@@ -47,12 +47,16 @@ trait ParameterizedGrailsUnitTest<T> extends GrailsUnitTest {
             def cutType = getTypeUnderTest()
             if (cutType != null) {
                 mockArtefact(cutType)
-                final String beanName = getBeanName(cutType)
+                def beanName = getBeanName(cutType)
                 if (beanName != null && applicationContext.containsBean(beanName)) {
-                    _artefactInstance = applicationContext.getBean(beanName, T)
+                    _artefactInstance = applicationContext.getBean(beanName, cutType)
                 } else {
-                    _artefactInstance = cutType.newInstance()
-                    applicationContext.autowireCapableBeanFactory.autowireBeanProperties(_artefactInstance, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false)
+                    _artefactInstance = cutType.getDeclaredConstructor().newInstance()
+                    applicationContext.autowireCapableBeanFactory.autowireBeanProperties(
+                            _artefactInstance,
+                            AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,
+                            false
+                    )
                 }
             }
         }
diff --git a/grails-testing-support-web/build.gradle b/grails-testing-support-web/build.gradle
index 71b2e2f789..4433c25c9f 100755
--- a/grails-testing-support-web/build.gradle
+++ b/grails-testing-support-web/build.gradle
@@ -53,6 +53,8 @@ dependencies {
     implementation project(':grails-web-jsp'), {
         // impl: TagLibraryResolverImpl
     }
+
+    compileOnly 'org.slf4j:slf4j-nop' // Get rid of warning about missing slf4j implementation during compilation
 }
 
 apply {
diff --git a/grails-testing-support-web/src/main/groovy/grails/testing/web/GrailsWebUnitTest.groovy b/grails-testing-support-web/src/main/groovy/grails/testing/web/GrailsWebUnitTest.groovy
index a0adf4b88f..3a82a059c6 100644
--- a/grails-testing-support-web/src/main/groovy/grails/testing/web/GrailsWebUnitTest.groovy
+++ b/grails-testing-support-web/src/main/groovy/grails/testing/web/GrailsWebUnitTest.groovy
@@ -25,6 +25,7 @@ import groovy.transform.CompileStatic
 import org.springframework.mock.web.MockHttpSession
 import org.springframework.mock.web.MockServletContext
 
+import grails.artefact.Controller
 import grails.artefact.TagLibrary
 import grails.core.GrailsClass
 import grails.core.GrailsControllerClass
@@ -55,15 +56,15 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
     GrailsWebRequest webRequest
 
     GrailsMockHttpServletRequest getRequest() {
-        return (GrailsMockHttpServletRequest) getWebRequest().getCurrentRequest()
+        webRequest.currentRequest as GrailsMockHttpServletRequest
     }
 
     GrailsMockHttpServletResponse getResponse() {
-        return (GrailsMockHttpServletResponse) getWebRequest().getCurrentResponse()
+        webRequest.currentResponse as GrailsMockHttpServletResponse
     }
 
     MockServletContext getServletContext() {
-        (MockServletContext) optionalServletContext
+        optionalServletContext as MockServletContext
     }
 
     Map<String, String> getViews() {
@@ -74,7 +75,7 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
      * The {@link org.springframework.mock.web.MockHttpSession} instance
      */
     MockHttpSession getSession() {
-        (MockHttpSession) request.session
+        request.session as MockHttpSession
     }
 
     /**
@@ -88,7 +89,7 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
      * The Grails 'params' object which is an instance of {@link grails.web.servlet.mvc.GrailsParameterMap}
      */
     GrailsParameterMap getParams() {
-        webRequest.getParams()
+        webRequest.params
     }
 
     /**
@@ -96,13 +97,13 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
      * @return
      */
     FlashScope getFlash() {
-        webRequest.getFlashScope()
+        webRequest.flashScope
     }
 
     @CompileDynamic
-    Object mockTagLib(Class<?> tagLibClass) {
-        GrailsTagLibClass tagLib = grailsApplication.addArtefact(TagLibArtefactHandler.TYPE, tagLibClass)
-        final tagLookup = applicationContext.getBean(TagLibraryLookup)
+    <T> T mockTagLib(Class<T> tagLibClass) {
+        def tagLib = grailsApplication.addArtefact(TagLibArtefactHandler.TYPE, tagLibClass) as GrailsTagLibClass
+        def tagLookup = applicationContext.getBean(TagLibraryLookup)
 
         if (!applicationContext.containsBean(tagLib.fullName)) {
             defineBeans {
@@ -118,17 +119,17 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
         TagLibraryMetaUtils.enhanceTagLibMetaClass(tagLib, tagLookup)
         TagLibraryMetaUtils.enhanceTagLibMetaClass(taglibObject.metaClass, tagLookup, tagLib.namespace)
         if (taglibObject instanceof TagLibrary) {
-            ((TagLibrary) taglibObject).setTagLibraryLookup(tagLookup)
+            ((TagLibrary) taglibObject).tagLibraryLookup = tagLookup
         }
-        taglibObject
+        taglibObject as T
     }
 
     @CompileDynamic
-    Object mockController(Class<?> controllerClass) {
+    <T extends Controller> T mockController(Class<T> controllerClass) {
         createAndEnhanceController(controllerClass)
         defineBeans {
             "$controllerClass.name"(controllerClass) { bean ->
-                bean.scope = 'prototype'
+                bean.scope = 'prototype' // A new instance is created for each request in tests to avoid state leakage between tests
                 bean.autowire = true
             }
         }
@@ -136,23 +137,26 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
         def controller = applicationContext.getBean(controllerClass.name)
 
         if (webRequest == null) {
-            throw new IllegalAccessException('Cannot access the controller outside of a request. Is the controller referenced in a where: block?')
+            throw new IllegalAccessException(
+                    'Cannot access the controller outside of a request. ' +
+                    'Is the controller referenced in a where: block?'
+            )
         }
 
         webRequest.request.setAttribute(GrailsApplicationAttributes.CONTROLLER, controller)
         webRequest.controllerName = GrailsNameUtils.getLogicalPropertyName(controller.class.name, ControllerArtefactHandler.TYPE)
 
-        controller
+        controller as T
     }
 
     private GrailsClass createAndEnhanceController(Class controllerClass) {
-        final GrailsControllerClass controllerArtefact = (GrailsControllerClass) grailsApplication.addArtefact(ControllerArtefactHandler.TYPE, controllerClass)
-        controllerArtefact.initialize()
-        return controllerArtefact
+        (grailsApplication.addArtefact(ControllerArtefactHandler.TYPE, controllerClass) as GrailsControllerClass).tap {
+            initialize()
+        }
     }
 
     void mockTagLibs(Class<?>... tagLibClasses) {
-        for (Class c : tagLibClasses) {
+        for (def c : tagLibClasses) {
             mockTagLib(c)
         }
     }
@@ -162,8 +166,9 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
             return
         }
         loadedCodecs << codecClass
-        DefaultGrailsCodecClass grailsCodecClass = new DefaultGrailsCodecClass(codecClass)
-        grailsCodecClass.configureCodecMethods()
+        def grailsCodecClass = new DefaultGrailsCodecClass(codecClass).tap {
+            configureCodecMethods()
+        }
         grailsApplication.addArtefact(CodecArtefactHandler.TYPE, grailsCodecClass)
         if (reinitialize) {
             applicationContext.getBean(DefaultCodecLookup).reInitialize()
@@ -180,22 +185,22 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
         String uri = null
         Map model
         if (args.containsKey('model')) {
-            model = (Map) args.model
+            model = args.model as Map
         } else {
             model = [:]
         }
-        final attributes = webRequest.attributes
+        def attributes = webRequest.attributes
         if (args.template) {
             uri = attributes.getTemplateUri(args.template as String, request)
         } else if (args.view) {
             uri = attributes.getViewUri(args.view as String, request)
         }
         if (uri != null) {
-            GroovyPagesTemplateEngine engine = applicationContext.getBean(GroovyPagesTemplateEngine)
-            final Template t = engine.createTemplate(uri)
-            if (t != null) {
+            def engine = applicationContext.getBean(GroovyPagesTemplateEngine)
+            def template = engine.createTemplate(uri)
+            if (template != null) {
                 def sw = new StringWriter()
-                renderTemplateToStringWriter(sw, t, model)
+                renderTemplateToStringWriter(sw, template, model)
                 return sw.toString()
             }
         }
@@ -222,21 +227,20 @@ trait GrailsWebUnitTest implements GrailsUnitTest {
      * @param contents The contents
      * @param model The model
      */
-    void applyTemplate(StringWriter sw, String template, Map params = [:]) {
+    void applyTemplate(StringWriter sw, String templateText, Map params = [:]) {
         def engine = applicationContext.getBean(GroovyPagesTemplateEngine)
-
-        def t = engine.createTemplate(template, 'test_' + System.currentTimeMillis(), false)
-        renderTemplateToStringWriter(sw, t, params)
+        def template = engine.createTemplate(templateText, 'test_' + System.currentTimeMillis(), false)
+        renderTemplateToStringWriter(sw, template, params)
     }
 
-    private renderTemplateToStringWriter(StringWriter sw, Template t, Map params) {
+    private renderTemplateToStringWriter(StringWriter sw, Template template, Map params) {
         if (!webRequest.controllerName) {
             webRequest.controllerName = 'test'
         }
         if (!webRequest.actionName) {
             webRequest.actionName = 'index'
         }
-        def w = t.make(params)
+        def w = template.make(params)
         def previousOut = webRequest.out
         try {
             def out = new GrailsPrintWriter(sw)
diff --git a/grails-testing-support-web/src/main/groovy/grails/testing/web/UrlMappingsUnitTest.groovy b/grails-testing-support-web/src/main/groovy/grails/testing/web/UrlMappingsUnitTest.groovy
index d4b3c07bd6..f15615cdd3 100644
--- a/grails-testing-support-web/src/main/groovy/grails/testing/web/UrlMappingsUnitTest.groovy
+++ b/grails-testing-support-web/src/main/groovy/grails/testing/web/UrlMappingsUnitTest.groovy
@@ -19,13 +19,16 @@
 package grails.testing.web
 
 import groovy.transform.CompileDynamic
+import groovy.transform.CompileStatic
 
 import junit.framework.AssertionFailedError
 import junit.framework.ComparisonFailure
+import org.jspecify.annotations.Nullable
 
+import grails.artefact.Controller
+import grails.core.DefaultGrailsApplication
 import grails.core.GrailsControllerClass
 import grails.web.UrlConverter
-import grails.web.mapping.UrlCreator
 import grails.web.mapping.UrlMappingInfo
 import grails.web.mapping.UrlMappingsHolder
 import org.grails.core.artefact.ControllerArtefactHandler
@@ -35,22 +38,24 @@ import org.grails.testing.ParameterizedGrailsUnitTest
 import org.grails.web.mapping.UrlMappingsHolderFactoryBean
 import org.grails.web.mapping.mvc.GrailsControllerUrlMappingInfo
 
+@CompileStatic
 trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnitTest {
 
     public static final String KEY_EXCEPTION = 'exception'
     private final List<String> assertionKeys = ['controller', 'action', 'view']
 
     Class[] getControllersToMock() {
-        []
+        new Class[0]
     }
 
+    @CompileDynamic
     void configuredMockedControllers() {
-        for (Class c : controllersToMock) {
-            final GrailsControllerClass controllerArtefact = (GrailsControllerClass) grailsApplication.addArtefact(ControllerArtefactHandler.TYPE, c)
+        for (def controllerClass : controllersToMock) {
+            def controllerArtefact = grailsApplication.addArtefact(ControllerArtefactHandler.TYPE, controllerClass) as GrailsControllerClass
             controllerArtefact.initialize()
             defineBeans {
-                "$controllerArtefact.name"(c) { bean ->
-                    bean.scope = 'prototype'
+                "$controllerArtefact.name"(controllerClass) { bean ->
+                    bean.scope = 'prototype' // A new instance is created for each request in tests to avoid state leakage between tests
                     bean.autowire = true
                 }
             }
@@ -65,7 +70,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * this restores the state expected by this spec.
      */
     void resetUrlMappingsForFeature() {
-        Class<T> typeUnderTest = getTypeUnderTest()
+        def typeUnderTest = getTypeUnderTest()
         if (typeUnderTest != null) {
             mockArtefact(typeUnderTest)
         }
@@ -80,7 +85,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      */
     @CompileDynamic
     void cleanupUrlMappingsAfterFeature() {
-        if (grailsApplication instanceof grails.core.DefaultGrailsApplication) {
+        if (grailsApplication instanceof DefaultGrailsApplication) {
             grailsApplication.@artefactInfo.remove(UrlMappingsArtefactHandler.TYPE)
         }
     }
@@ -96,30 +101,29 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * Maps a URI and returns the appropriate controller instance
      *
      * @param uri The URI to map
-     * @return The controller instance
+     * @return The controller instance or null if no mapping was found
      */
-    Object mapURI(String uri) {
-        UrlMappingsHolder mappingsHolder = getUrlMappingsHolder()
-
-        UrlMappingInfo[] mappingInfos = mappingsHolder.matchAll(uri, request.method)
-        for (UrlMappingInfo info in mappingInfos) {
+    @Nullable Controller mapURI(String uri) {
+        def mappingsHolder = getUrlMappingsHolder()
+        def mappingInfos = mappingsHolder.matchAll(uri, request.method)
+        for (def info : mappingInfos) {
             def backupParams = new HashMap(webRequest.params)
             info.configure(webRequest)
-
             webRequest.params.putAll(backupParams)
             if (info.viewName == null && info.URI == null) {
                 if (info instanceof GrailsControllerUrlMappingInfo) {
                     def controller = info.controllerClass
                     if (controller != null) {
-                        return applicationContext.getBean(controller.name)
+                        return applicationContext.getBean(controller.name) as Controller
                     }
                 }
             }
         }
+        null
     }
 
     private boolean checkController(String controller, boolean throwEx) {
-        final controllerClass = getControllerClass(controller)
+        def controllerClass = getControllerClass(controller)
         if (!controllerClass && throwEx) {
             throw new AssertionFailedError("Url mapping assertion failed, '$controller' is not a valid controller")
         }
@@ -137,16 +141,16 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
     }
 
     /**
-     * @param controller The controller name
+     * @param controllerName The controller name
      * @param url The url
      * @return true If a controller exists for the specified name and url
      */
-    boolean verifyController(String controller) {
-        checkController(controller, false)
+    boolean verifyController(String controllerName) {
+        checkController(controllerName, false)
     }
 
     private boolean checkAction(String controller, String action, boolean throwEx) {
-        final controllerClass = getControllerClass(controller)
+        def controllerClass = getControllerClass(controller)
         boolean valid = controllerClass?.mapsToURI("/$controller/$action")
         if (!valid && throwEx) {
             throw new AssertionFailedError("Url mapping assertion failed, '$action' is not a valid action of controller '$controller'")
@@ -174,18 +178,20 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
     }
 
     private boolean checkView(String controller, String view, boolean throwEx) {
-        def pathPattern =  ((controller) ? "$controller/" : '') + "${view}.gsp"
+        String pathPattern =  ((controller) ? "$controller/" : '') + "${view}.gsp"
         if (!pathPattern.startsWith('/')) {
             pathPattern = "/$pathPattern"
         }
-        GroovyPagesTemplateEngine templateEngine = applicationContext.getBean('groovyPagesTemplateEngine', GroovyPagesTemplateEngine)
+        def templateEngine = applicationContext.getBean('groovyPagesTemplateEngine', GroovyPagesTemplateEngine)
 
-        def t = templateEngine.createTemplate(pathPattern)
-        if (!t && throwEx) {
+        def template = templateEngine.createTemplate(pathPattern)
+        if (!template && throwEx) {
             throw new AssertionFailedError(
-                    (controller) ? "Url mapping assertion failed, '$view' is not a valid view of controller '$controller'" : "Url mapping assertion failed, '$view' is not a valid view")
+                    (controller)
+                            ? "Url mapping assertion failed, '$view' is not a valid view of controller '$controller'"
+                            : "Url mapping assertion failed, '$view' is not a valid view")
         }
-        t != null
+        template != null
     }
 
     /**
@@ -225,7 +231,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * @param paramAssertions The parameters to assert defined in the body of the closure
      */
     void assertUrlMapping(Map<String, String> assertions, String url, Closure paramAssertions = null) {
-        assertForwardUrlMapping(assertions, url, paramAssertions)
+        assertForwardUrlMapping(assertions as Map<String, Object>, url, paramAssertions)
         if (assertions.controller && !(url instanceof Integer)) {
             assertReverseUrlMapping(assertions, url, paramAssertions)
         }
@@ -249,13 +255,14 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * @return True if the url matches the assertions
      */
     boolean verifyUrlMapping(Map<String, String> assertions, String url, Closure paramAssertions = null) {
-        boolean returnValue = verifyForwardUrlMapping(assertions, url, paramAssertions)
+        boolean returnValue = verifyForwardUrlMapping(assertions as Map<String, Object>, url, paramAssertions)
         if (assertions.controller && !(url instanceof Integer)) {
             returnValue = returnValue && verifyReverseUrlMapping(assertions, url, paramAssertions)
         }
         returnValue
     }
 
+    @CompileDynamic
     private boolean checkForwardUrlMapping(Map<String, Object> assertions, Object url, Closure paramAssertions, boolean throwEx) {
 
         UrlMappingsHolder mappingsHolder = getUrlMappingsHolder()
@@ -291,7 +298,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
             if (mapping) mappingInfos << mapping
         }
         else {
-            mappingInfos = mappingsHolder.matchAll((String) url, request.method).toList()
+            mappingInfos = mappingsHolder.matchAll((String) url, request.method) as List<UrlMappingInfo>
         }
 
         if (mappingInfos.size() == 0) {
@@ -321,7 +328,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
                             break
                         case 'action':
                             if (key == 'action' && actual == null) {
-                                final controllerClass = getControllerClass(assertions.controller)
+                                def controllerClass = getControllerClass(assertions.controller as String)
                                 actual = controllerClass?.defaultAction
                             }
                             break
@@ -329,7 +336,9 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
 
                     if (expected != actual) {
                         if (throwEx) {
-                            throw new ComparisonFailure("Url mapping $key assertion for '$url' failed".toString(), expected, actual)
+                            throw new ComparisonFailure(
+                                    "Url mapping $key assertion for '$url' failed", expected, actual
+                            )
                         } else {
                             returnVal = false
                         }
@@ -347,7 +356,9 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
 
                     if (expected != actual) {
                         if (throwEx) {
-                            throw new ComparisonFailure("Url mapping $name assertion for '$url' failed".toString(), expected, actual)
+                            throw new ComparisonFailure(
+                                    "Url mapping $name assertion for '$url' failed", expected, actual
+                            )
                         } else {
                             returnVal = false
                         }
@@ -372,15 +383,16 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
     }
 
     private boolean checkReverseUrlMapping(Map<String, String> assertions, String url, Closure paramAssertions, boolean throwEx) {
-        UrlMappingsHolder mappingsHolder = applicationContext.getBean('grailsUrlMappingsHolder', UrlMappingsHolder)
-        UrlConverter urlConverter = applicationContext.getBean(UrlConverter.BEAN_NAME, UrlConverter)
+        def mappingsHolder = applicationContext.getBean('grailsUrlMappingsHolder', UrlMappingsHolder)
+        def urlConverter = applicationContext.getBean(UrlConverter.BEAN_NAME, UrlConverter)
         def controller = assertions.controller
         def action = assertions.action
         def method = assertions.method
         def plugin = assertions.plugin
         def namespace = assertions.namespace
 
-        String convertedControllerName = null, convertedActionName = null
+        String convertedControllerName = null
+        String convertedActionName = null
 
         if (controller) convertedControllerName = urlConverter.toUrlElement(controller) ?: controller
         if (action) convertedActionName = urlConverter.toUrlElement(action) ?: action
@@ -391,7 +403,7 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
             paramAssertions.resolveStrategy = Closure.DELEGATE_ONLY
             paramAssertions.call()
         }
-        UrlCreator urlCreator = mappingsHolder.getReverseMapping(controller, action, namespace, plugin, method, params)
+        def urlCreator = mappingsHolder.getReverseMapping(controller, action, namespace, plugin, method, params)
         if (urlCreator == null) {
             if (throwEx) {
                 throw new AssertionFailedError("could not create reverse mapping of '$url' for {controller = $controller, action = $action, params = $params}")
@@ -399,11 +411,14 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
                 return false
             }
         }
-        String createdUrl = urlCreator.createRelativeURL(convertedControllerName, convertedActionName, params, 'UTF-8')
-
+        def createdUrl = urlCreator.createRelativeURL(convertedControllerName, convertedActionName, params, 'UTF-8')
         if (url != createdUrl) {
             if (throwEx) {
-                throw new ComparisonFailure("reverse mapping assertion for {controller = $controller, action = $action, params = $params}", url, createdUrl)
+                throw new ComparisonFailure(
+                        "reverse mapping assertion for {controller = $controller, action = $action, params = $params}",
+                        url,
+                        createdUrl
+                )
             } else {
                 return false
             }
@@ -445,15 +460,15 @@ trait UrlMappingsUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
         checkReverseUrlMapping(assertions, url, paramAssertions, false)
     }
 
-    GrailsControllerClass getControllerClass(String controller) {
-        (GrailsControllerClass) grailsApplication.getArtefactByLogicalPropertyName(ControllerArtefactHandler.TYPE, controller)
+    GrailsControllerClass getControllerClass(String controllerName) {
+        grailsApplication.getArtefactByLogicalPropertyName(ControllerArtefactHandler.TYPE, controllerName) as GrailsControllerClass
     }
 
     @CompileDynamic
     void mockArtefact(Class<?> urlMappingsClass) {
         // Clear any URL mapping artefacts registered by another spec so that this spec's
         // reverse-mapping lookups don't see foreign mappings. addArtefact() only appends.
-        if (grailsApplication instanceof grails.core.DefaultGrailsApplication) {
+        if (grailsApplication instanceof DefaultGrailsApplication) {
             grailsApplication.@artefactInfo.remove(UrlMappingsArtefactHandler.TYPE)
         }
         grailsApplication.addArtefact(UrlMappingsArtefactHandler.TYPE, urlMappingsClass)
diff --git a/grails-testing-support-web/src/main/groovy/grails/testing/web/controllers/ControllerUnitTest.groovy b/grails-testing-support-web/src/main/groovy/grails/testing/web/controllers/ControllerUnitTest.groovy
index a198f8814b..97b800ca5e 100755
--- a/grails-testing-support-web/src/main/groovy/grails/testing/web/controllers/ControllerUnitTest.groovy
+++ b/grails-testing-support-web/src/main/groovy/grails/testing/web/controllers/ControllerUnitTest.groovy
@@ -18,11 +18,13 @@
  */
 package grails.testing.web.controllers
 
+import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
-import groovy.transform.TypeCheckingMode
 
 import javassist.util.proxy.ProxyFactory
+import org.jspecify.annotations.Nullable
 
+import grails.artefact.Controller
 import grails.testing.web.GrailsWebUnitTest
 import grails.web.mime.MimeType
 import org.grails.testing.ParameterizedGrailsUnitTest
@@ -51,27 +53,25 @@ trait ControllerUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWeb
     /**
      * @return The model of the current controller
      */
-    @CompileStatic(TypeCheckingMode.SKIP)
+    @CompileDynamic
     Map getModel() {
         Map model = request.getAttribute(GrailsApplicationAttributes.CONTROLLER)?.modelAndView?.model
         if (model == null) {
-            model = request.getAttribute(GrailsApplicationAttributes.TEMPLATE_MODEL)
+            model = request.getAttribute(GrailsApplicationAttributes.TEMPLATE_MODEL) as Map
         }
-        return model ?: [:]
+        model ?: [:]
     }
 
     /**
      * @return The view of the current controller
      */
-    @CompileStatic(TypeCheckingMode.SKIP)
-    String getView() {
-        final controller = request.getAttribute(GrailsApplicationAttributes.CONTROLLER)
-
-        final viewName = controller?.modelAndView?.viewName
+    @CompileDynamic
+    @Nullable String getView() {
+        def controller = request.getAttribute(GrailsApplicationAttributes.CONTROLLER)
+        def viewName = controller?.modelAndView?.viewName
         if (viewName != null) {
             return viewName
         }
-
         if (webRequest.controllerName && webRequest.actionName) {
             new GroovyPagesUriSupport().getViewURI(webRequest.controllerName, webRequest.actionName)
         } else {
@@ -86,7 +86,7 @@ trait ControllerUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWeb
      * @return An instance of the controller
      */
     void mockArtefact(Class<?> controllerClass) {
-        mockController(controllerClass)
+        mockController(controllerClass.asSubclass(Controller) as Class<Controller>)
     }
 
     String getBeanName(Class<?> controllerClass) {
diff --git a/grails-testing-support-web/src/main/groovy/grails/testing/web/interceptor/InterceptorUnitTest.groovy b/grails-testing-support-web/src/main/groovy/grails/testing/web/interceptor/InterceptorUnitTest.groovy
index 23f2950e34..f6ba421be5 100644
--- a/grails-testing-support-web/src/main/groovy/grails/testing/web/interceptor/InterceptorUnitTest.groovy
+++ b/grails-testing-support-web/src/main/groovy/grails/testing/web/interceptor/InterceptorUnitTest.groovy
@@ -16,19 +16,18 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
 package grails.testing.web.interceptor
 
 import java.lang.reflect.ParameterizedType
 
 import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
-import groovy.transform.TypeCheckingMode
+
+import org.jspecify.annotations.Nullable
 
 import org.springframework.web.servlet.ModelAndView
 
 import grails.artefact.Interceptor
-import grails.core.GrailsClass
 import grails.testing.web.GrailsWebUnitTest
 import grails.util.GrailsNameUtils
 import grails.web.mapping.UrlMappingInfo
@@ -51,8 +50,8 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * @return The mocked interceptor
      */
     @CompileDynamic
-    Interceptor mockInterceptor(Class<?> interceptorClass) {
-        GrailsClass artefact = grailsApplication.addArtefact(InterceptorArtefactHandler.TYPE, interceptorClass)
+    <I extends Interceptor> I mockInterceptor(Class<I> interceptorClass) {
+        def artefact = grailsApplication.addArtefact(InterceptorArtefactHandler.TYPE, interceptorClass)
         defineBeans {
             "${artefact.propertyName}"(artefact.clazz) { bean ->
                 bean.autowire = true
@@ -60,7 +59,7 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
         }
         getHandlerInterceptor()
                 .setInterceptors(applicationContext.getBeansOfType(Interceptor).values() as Interceptor[])
-        (Interceptor) applicationContext.getBean(artefact.propertyName, interceptorClass)
+        applicationContext.getBean(artefact.propertyName, interceptorClass)
     }
 
     /**
@@ -70,14 +69,12 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      * @param callable A callable containing an invocation of a controller action
      * @return The result of the callable execution
      */
-    Object withInterceptors(Map<String, Object> arguments, Closure callable) {
+    @Nullable Object withInterceptors(Map<String, Object> arguments, Closure callable) {
         ensureInterceptorHasBeenMocked()
-        UrlMappingInfo info = withRequest(arguments)
-
-        def hi = getHandlerInterceptor()
-
+        def urlMappingInfo = withRequest(arguments)
+        def handlerInterceptor = getHandlerInterceptor()
         try {
-            if (hi.preHandle(request, response, this)) {
+            if (handlerInterceptor.preHandle(request, response, this)) {
                 def result = callable.call()
                 ModelAndView modelAndView = null
                 def modelAndViewObject = request.getAttribute(GrailsApplicationAttributes.MODEL_AND_VIEW)
@@ -85,17 +82,18 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
                     modelAndView = (ModelAndView) modelAndViewObject
                 }
                 else if (result instanceof Map) {
-                    modelAndView =  new ModelAndView(info?.actionName ?: 'index', new HashMap<String, Object>((Map) result))
+                    modelAndView =  new ModelAndView(urlMappingInfo?.actionName ?: 'index', new HashMap<String, Object>((Map) result))
                 }
                 else if (result instanceof ModelAndView) {
                     return (ModelAndView) result
                 }
-                hi.postHandle(request, response, this, modelAndView)
+                handlerInterceptor.postHandle(request, response, this, modelAndView)
                 return result
             }
         } catch (Exception e) {
-            hi.afterCompletion(request, response, this, e)
+            handlerInterceptor.afterCompletion(request, response, this, e)
         }
+        null
     }
 
     /**
@@ -106,21 +104,21 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
      *
      * @return The {@link UrlMappingInfo} object
      */
-    @CompileStatic(TypeCheckingMode.SKIP)
+    @CompileDynamic
     UrlMappingInfo withRequest(Map<String, Object> arguments) {
         ensureInterceptorHasBeenMocked()
-        UrlMappingInfo info = null
+        UrlMappingInfo urlMappingInfo = null
         if (arguments.uri) {
             request.requestURI = arguments.uri.toString()
         } else {
-            info = new ForwardUrlMappingInfo(arguments)
-            request.setAttribute(UrlMappingsHandlerMapping.MATCHED_REQUEST, info)
+            urlMappingInfo = new ForwardUrlMappingInfo(arguments)
+            request.setAttribute(UrlMappingsHandlerMapping.MATCHED_REQUEST, urlMappingInfo)
         }
 
-        for (String name in request.attributeNames.findAll() { String n -> n.endsWith(InterceptorArtefactHandler.MATCH_SUFFIX) }) {
+        for (String name : request.attributeNames.findAll { String candidate -> candidate.endsWith(InterceptorArtefactHandler.MATCH_SUFFIX) }) {
             request.removeAttribute(name)
         }
-        info
+        urlMappingInfo
     }
 
     private GrailsInterceptorHandlerInterceptorAdapter getHandlerInterceptor() {
@@ -136,12 +134,11 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
     }
 
     private Class<T> getInterceptorTypeUnderTest() {
-        ParameterizedType parameterizedType = (ParameterizedType) getClass().genericInterfaces.find { genericInterface ->
+        def parameterizedType = getClass().genericInterfaces.find { genericInterface ->
             genericInterface instanceof ParameterizedType &&
                     InterceptorUnitTest.isAssignableFrom((Class)((ParameterizedType)genericInterface).rawType)
-        }
-
-        parameterizedType?.actualTypeArguments[0]
+        } as ParameterizedType
+        parameterizedType?.actualTypeArguments[0] as Class<T>
     }
 
     T getInterceptor() {
@@ -151,7 +148,7 @@ trait InterceptorUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWe
 
     private void ensureInterceptorHasBeenMocked() {
         if (!hasBeenMocked) {
-            mockInterceptor(getInterceptorTypeUnderTest())
+            mockInterceptor(getInterceptorTypeUnderTest() as Class<Interceptor>)
             hasBeenMocked = true
         }
     }
diff --git a/grails-testing-support-web/src/main/groovy/grails/testing/web/taglib/TagLibUnitTest.groovy b/grails-testing-support-web/src/main/groovy/grails/testing/web/taglib/TagLibUnitTest.groovy
index 799b13211e..85ae999e08 100644
--- a/grails-testing-support-web/src/main/groovy/grails/testing/web/taglib/TagLibUnitTest.groovy
+++ b/grails-testing-support-web/src/main/groovy/grails/testing/web/taglib/TagLibUnitTest.groovy
@@ -22,15 +22,18 @@ import java.lang.reflect.ParameterizedType
 
 import groovy.transform.CompileStatic
 
+import grails.artefact.TagLibrary
 import grails.core.gsp.GrailsTagLibClass
 import grails.testing.web.GrailsWebUnitTest
-import org.grails.testing.ParameterizedGrailsUnitTest
 import org.grails.taglib.TagLibraryLookup
+import org.grails.testing.ParameterizedGrailsUnitTest
 
 @CompileStatic
 trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnitTest {
 
-    private static final Map<Class<?>, Set<Class<?>>> MOCKED_TAG_LIB_CLASSES_BY_SPEC = [:].withDefault { [] as LinkedHashSet<Class<?>> }
+    private static final Map<Class<T>, Set<Class<?>>> MOCKED_TAG_LIB_CLASSES_BY_SPEC = [:].withDefault {
+        [] as LinkedHashSet<Class<?>>
+    }
     private boolean hasBeenMocked = false
 
     /**
@@ -45,9 +48,9 @@ trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnit
         super.applyTemplate(contents, model)
     }
 
-    void applyTemplate(StringWriter sw, String template, Map params = [:]) {
+    void applyTemplate(StringWriter sw, String templateText, Map params = [:]) {
         ensureTaglibHasBeenMocked()
-        super.applyTemplate(sw, template, params)
+        super.applyTemplate(sw, templateText, params)
     }
 
     /**
@@ -58,16 +61,16 @@ trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnit
      * @return The tag library instance
      */
     void mockArtefact(Class<?> tagLibClass) {
-        mockTagLib(tagLibClass)
+        mockTagLib((Class<? extends TagLibrary>) tagLibClass)
     }
 
-    Object mockTagLib(Class<?> tagLibClass) {
+    <U> U mockTagLib(Class<U> tagLibClass) {
         getMockedTagLibClasses().add(tagLibClass)
-        GrailsWebUnitTest.super.mockTagLib(tagLibClass)
+        (U) GrailsWebUnitTest.super.mockTagLib(tagLibClass)
     }
 
     void mockTagLibs(Class<?>... tagLibClasses) {
-        for (Class<?> tagLibClass in tagLibClasses) {
+        for (def tagLibClass : tagLibClasses) {
             mockTagLib(tagLibClass)
         }
     }
@@ -77,17 +80,16 @@ trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnit
     }
 
     private Class<T> getTagLibTypeUnderTest() {
-        ParameterizedType parameterizedType = (ParameterizedType) getClass().genericInterfaces.find { genericInterface ->
+        def parameterizedType = getClass().genericInterfaces.find { genericInterface ->
             genericInterface instanceof ParameterizedType &&
-                    TagLibUnitTest.isAssignableFrom((Class)((ParameterizedType)genericInterface).rawType)
-        }
-
-        parameterizedType?.actualTypeArguments[0]
+                    TagLibUnitTest.isAssignableFrom((Class) ((ParameterizedType) genericInterface).rawType)
+        } as ParameterizedType
+        parameterizedType?.actualTypeArguments[0] as Class<T>
     }
 
     T getTagLib() {
         ensureTaglibHasBeenMocked()
-        getArtefactInstance()
+        artefactInstance
     }
 
     private void ensureTaglibHasBeenMocked() {
@@ -97,7 +99,7 @@ trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnit
             if (tagLibTypeUnderTest != null) {
                 mockedTagLibClasses.add(tagLibTypeUnderTest)
             }
-            for (Class<?> tagLibClass in mockedTagLibClasses) {
+            for (def tagLibClass : mockedTagLibClasses) {
                 GrailsWebUnitTest.super.mockTagLib(tagLibClass)
             }
             hasBeenMocked = true
@@ -105,20 +107,20 @@ trait TagLibUnitTest<T> implements ParameterizedGrailsUnitTest<T>, GrailsWebUnit
     }
 
     private boolean areMockedTagLibsRegistered() {
-        TagLibraryLookup tagLibraryLookup = applicationContext.getBean(TagLibraryLookup)
-        for (Class<?> tagLibClass in getMockedTagLibClasses()) {
-            GrailsTagLibClass grailsTagLibClass = (GrailsTagLibClass) grailsApplication.getArtefact('TagLib', tagLibClass.name)
+        def tagLibraryLookup = applicationContext.getBean(TagLibraryLookup)
+        for (def tagLibClass : getMockedTagLibClasses()) {
+            def grailsTagLibClass = grailsApplication.getArtefact('TagLib', tagLibClass.name) as GrailsTagLibClass
             if (grailsTagLibClass == null) {
                 return false
             }
-            String namespace = grailsTagLibClass.namespace
-            if (!grailsTagLibClass.tagNames.every { String tagName ->
+            def namespace = grailsTagLibClass.namespace
+            if (!grailsTagLibClass.tagNames.every { tagName ->
                 tagLibraryLookup.lookupTagLibrary(namespace, tagName) != null
             }) {
                 return false
             }
         }
-        return true
+        true
     }
 
     private Set<Class<?>> getMockedTagLibClasses() {