Comprehensive test coverage for the nine Grails 8 v8 sample-app guides - #510
Merged
Conversation
Every Spock layer the guide demonstrates now compiles and passes against Grails 8.0.0-SNAPSHOT (16 unit + 4 integration/functional/Geb green). The specs were authored but never executed and several were broken. Snippets re-vendored from the fixed sample app: - BookSpec: getFieldError(...), the nullable-vs-blank map-constructor gotcha, added blank + maxSize constraint cases - BookServiceSpec: flush seeded books so the GORM finders see them - BookControllerSpec: RestfulController.show() via params.id (no-arg action) - BookIntegrationSpec: flush before the duplicate save; a @service save throws ValidationException - BookController / UrlMappings / book/index.gsp: render an HTML table at /books - BookFunctionalSpec: seed committed data via withNewTransaction Prose corrected where it described code that never ran or made false claims: - domainUnitTest: getFieldError + the nullable-vs-blank gotcha - integrationTest: Testcontainers PostgreSQL (not H2); @service save throws ValidationException (not hasErrors) - functionalTest: new "page the test drives" section + committed-seed rationale - platform: jacoco executionData uses layout.buildDirectory, not deprecated buildDir - whatYouWillBuild: index action also renders an HTML table Assisted-by: claude-code:claude-opus-4-7
The sample app did not compile against Grails 8.0.0-SNAPSHOT and had no tests. It now builds and ships 27 green tests (19 unit + 8 integration/ functional). Re-vendored snippets and corrected prose: - BootStrap snippet: drop the manual Logger field that collides with the injected log property (compile failure); bootstrapData chapter now describes the Author.withTransaction wrapper, not @transactional - BookController snippet: bulkCreate uses the injected MessageSource (the message() taglib is not available in a rest-api controller), so an invalid bulk POST returns the documented 422 instead of a 500 New "Testing the API" chapter (registered in conf/guides.yml toc) with vendored snippets: - application-test.yml: testcontainers PostgreSQL + create-drop schema + databasemigration.updateOnStart false (the database-migration plugin otherwise blocks schema creation in the test env) - BookSpec / AuthorSpec: domain constraints and the hasMany mapping - BookControllerSpec: pagination clamping - BookIntegrationSpec: filtering / paging / relationships (seed-aware) - BookFunctionalSpec: the JSON surface over HTTP incl. the all-or-nothing bulkCreate contract - documents the testcontainers-bom and grails-testing-support-web deps Assisted-by: claude-code:claude-opus-4-7
The CI workflow ran ./gradlew functionalTest, but the guide never showed the task that defines it and the app had no unit or integration tests, so two of the four stages were hollow and the functional stage would fail. The sample app now runs 6 green tests across the three stages (3 unit + 2 integration + 1 Geb functional). Re-vendored snippets and expanded the testing chapters: - unitTests: adds the Message domain + MessageSpec (DomainUnitTest) - integrationTests: adds MessageIntegrationSpec and explains the integrationTest -> *IntegrationSpec / functionalTest -> *FunctionalSpec split - functionalTests: documents the build.gradle wiring that actually defines the functionalTest task (a second Test task over the integration-test source set, filtered to the Geb specs) and shows HomePageFunctionalSpec Assisted-by: claude-code:claude-opus-4-7
The HTMX interactions never worked: createLink(controller, action) reverse mapping is ambiguous for the shared /tasks and /tasks/$id REST routes and fell back to /task/create, which no mapping serves, so every hx-post/patch/delete 404'd. The sample app now drives all five swap types green (5 unit + 3 integration + 6 Geb functional). - re-vendored the view partials to build HTMX URLs with createLink(uri: '/tasks...') so they match the REST mappings - urlMappings chapter: explains why createLink(uri:) is used instead of controller/action reverse mapping for the shared REST routes - new "Testing the HTMX UI" chapter (registered in conf/guides.yml toc): TaskSpec (constraints), TaskIntegrationSpec (findAllByTitleIlike, ordering, toggle), TaskTrackerFunctionalSpec (Geb tests of every HTMX swap, with withConfirm for delete) Assisted-by: claude-code:claude-opus-4-7
The sample app's only test was the forge default (a 'Welcome to Grails' title assertion that never matched the actual 'Welcome to Grails + Tailwind' page), so it had never run. The app now has a real browser test suite (5 Geb specs). - new "Testing the Tailwind Build" chapter (registered in conf/guides.yml toc) vendoring TailwindFunctionalSpec: asserts the compiled app.css is served, that .card/.btn-primary apply real computed styles from the @apply layer, and that the class-based dark-mode toggle works and persists to localStorage Assisted-by: claude-code:claude-opus-4-7
The SPA-serving chain was broken end to end and the backend had no tests. The sample app now runs 15 green tests (7 unit + 8 integration/functional) and genuinely serves the SPA same-origin. Re-vendored snippets and corrected prose: - index.gson: render the book template with collection: (it was building items as JSON strings) and declare Integer bookCount (was Long -> 500) - SpaController: serve index.html from the classpath (forward url: 404s in a rest-api app); spaController chapter rewritten to match, toc title updated - new AssetController + /assets/** mapping streams the Vite bundle same-origin (the rest-api profile serves no static resources); urlMappings chapter updated to document both routes New "Testing the API and SPA Serving" chapter (registered in conf/guides.yml toc) vendoring BookSpec, BookControllerSpec, BookIntegrationSpec, and ApiFunctionalSpec (JSON envelope, POST 201/422, same-origin no-CORS, and the SPA shell + /assets bundle served same-origin - which also exercises the Vite build + copy integration). Assisted-by: claude-code:claude-opus-4-7
…ting chapter Three custom-widget/table rendering bugs threw at runtime and the seed many-to-many never persisted. The sample app now renders all custom widgets and runs 20 green tests (12 unit + 8 integration/Geb). Re-vendored snippets and added prose: - isbn _widget.gsp: doubled the backslashes in the pattern attribute (\d is an illegal Groovy escape in a GSP attribute, so create/edit threw); bookIsbn chapter gains a note explaining the GSP escaping - templates/_fields/_table.gsp: domainClass.javaClass.simpleName (was domainClass.simpleName, which threw on HibernatePersistentEntity) - this now matches the appWideTable chapter's own prose - BootStrap: wrap the seed in Book.withTransaction so the Book<->Tag join persists; bootstrapData chapter explains why New "Testing the Widgets and Wrappers" chapter (registered in conf/guides.yml toc): BookSpec/AuthorSpec (constraints + associations), LibraryIntegrationSpec (m2m + one-to-one), LibraryFunctionalSpec (Geb tests that the custom f:table, display widgets, ISBN widget, and association widgets actually render). Assisted-by: claude-code:claude-opus-4-7
…nippets
The sample app diverged from the guide (the Dev Reload chapter documents the
grails-exploded plugin + grails { plugins { } } block, but the apps used a
plain dependency), the admin REST surface did not route, and the modules had
only the forge-default Geb spec. The sample now runs 10 green tests (6 unit +
4 functional) and matches the documented setup.
Expanded testingPerModule with vendored snippets:
- shared-core BookSpec / BookServiceSpec (the shared domain + @service)
- webapp-admin UrlMappings (the new "/books"(resources:'book') so the
RestfulController routes) + AdminBookFunctionalSpec (REST CRUD over HTTP)
- webapp-customer CatalogFunctionalSpec (the read-only catalog)
The Dev Reload and Webapps chapters already documented the exploded-plugin +
grails.plugins wiring; the sample app now actually implements it, so the
guide's primary reload recipe works as written.
Assisted-by: claude-code:claude-opus-4-7
The app shipped only the forge-default Geb spec and its test build could not resolve Testcontainers. The sample now runs 4 green functional tests over the Actuator health surface the guide configures. New "Testing the Health Probes" chapter (registered in conf/guides.yml toc) vendoring: - application-test.yml: testcontainers-bom dependency note + create-drop schema and databasemigration.updateOnStart false (the database-migration plugin otherwise blocks schema creation in the test env) - ActuatorHealthFunctionalSpec: /actuator/health, liveness and readiness all UP (readiness against Testcontainers PostgreSQL), and env/metrics stay closed - the same readiness endpoint the Compose HEALTHCHECK polls Assisted-by: claude-code:claude-opus-4-7
Contributor
There was a problem hiding this comment.
Pull request overview
Adds comprehensive layered test coverage (unit, integration, functional/Geb) for the nine Grails 8 (v8) sample-app guides, mirroring fixes into the vendored snippets/ trees and adding a new Testing chapter per guide. Several guides also receive functional fixes (route mappings, controller/view wiring, BootStrap transactions, gson view model) so the documented sample apps actually compile, run, and serve their endpoints correctly.
Changes:
- New
testing.adocchapters (and supportingBookSpec/*IntegrationSpec/*FunctionalSpecsnippets) for each of the 9 v8 guides, registered inconf/guides.yml. - Source/snippet fixes uncovered by the new tests: HTMX
createLink(uri:)workarounds, SPA serving viaResourceLoader+ newAssetController, gson view fix, custom-widget GSP\\dpattern escape,domainClass.javaClass.simpleName, BootStrap transactions, multi-moduleUrlMappings+ admin REST mapping,MessageSourceforbulkCreateerrors, etc. - Minor guide prose updates explaining the fixes (urlMappings notes, spaController rewrite, bootstrapData note, integrationTest/functionalTest stage wiring, JaCoCo
layout.buildDirectorymigration).
Reviewed changes
Copilot reviewed 73 out of 73 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| guides/grails-vite-spa/v8/snippets/backend/** | New SPA AssetController, SpaController rewrite, gson view fix, unit/integration/functional specs. |
| guides/grails-vite-spa/v8/guide/{urlMappings,spaController,testing}.adoc | Prose updates and new testing chapter. |
| guides/grails-tailwindcss/v8/** | New Geb functional spec + testing chapter. |
| guides/grails-spock-test-tour/v8/** | Expanded unit/controller/integration/functional specs; new GSP/BootStrap/UrlMappings snippets; BookController HTML format. |
| guides/grails-rest-library/v8/** | Removed Logger from BootStrap, MessageSource for errors, new layered specs + testing chapter, test-profile yml. |
| guides/grails-multi-module/v8/** | New shared-core unit specs, admin UrlMappings, admin/customer functional specs, testing chapter. |
| guides/grails-htmx/v8/** | createLink(uri:) workaround in 4 GSPs, new Task domain/integration/Geb specs, urlMappings note, testing chapter. |
| guides/grails-github-actions-cicd/v8/** | New Message domain, layered specs, unit/integration/functional chapter additions. |
| guides/grails-fields-custom-widgets-and-wrappers/v8/** | javaClass.simpleName fix, ISBN \\d escape, transactional BootStrap, layered specs + testing chapter. |
| guides/grails-docker-bootbuildimage/v8/** | Actuator health functional spec + testing chapter + test-profile yml. |
| conf/guides.yml | Registers new testing toc entry per touched guide and retitles vite-spa spaController chapter. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Comprehensive, layered test coverage for the nine Grails 8 (
v8) sample-app guides, following the worked example set bygrails-transactional-events. Every sample app was cloned, built, and run against the current Grails 8.0.0-SNAPSHOT stack (Spring Boot 4.0.5, Groovy 4.0.32, JDK 21). All nine were broken or untested in some way - the guide rendering pipeline only checks YAML + AsciiDoc +include::resolution, never compiling or running the sample app, so a guide can render perfectly and be non-functional.Each guide's upstream sample app (
grails-guides/<name>@ grails8) now has a greentest+integrationTestsuite, the fixes are mirrored into the vendoredsnippets/, and prose was corrected wherever it described code that never ran. ATestingchapter was added where the guide lacked one.Per-guide results
getFieldError; flush;show()viaparams.id; HTML view for Geb; committed seed; JaCoCoLogger, gsonDate.format);bulkCreate500→422 viaMessageSource; testcontainers-bom; web test supportfunctionalTesttask that did not exist; wired it + a real Message domain so all 4 stages run real testscreateLinkreverse-mapping) →createLink(uri:); Geb tests for every swap.card/.btn-primaryapply, dark-mode toggle persistsLongcount 500;forward404 → classpath render;/assets/**404 →AssetControllerpattern\dGSP-escape;domainClass.simpleName→javaClass.simpleName; m2m seed never persisted (transaction)grails-exploded+grails { plugins }dev-reload path; admin REST routing (resourcesmapping)Verification
./gradlew testand./gradlew integrationTestgreen (Docker + Testcontainers PostgreSQL/Selenium where applicable). Sample apps pushed tograils-guides/<name> @ grails8../gradlew validateGuides -PvalidationMode=both: 93 guides parsed, 0 errors.include::directives resolved (renderGuide_<name>_8 --no-configuration-cache).Scope is limited to the nine
guides/<name>/v8trees plusconf/guides.yml(newtestingchapters registered in the toc).Assisted-by: claude-code:claude-opus-4-7