Skip to content

Run the SQL Server integration tests on Testcontainers, with a CI job - #390

Merged
lahma merged 7 commits into
mainfrom
4x/testcontainers-integration
Nov 3, 2026
Merged

lahma merged 7 commits into
mainfrom
4x/testcontainers-integration

Conversation

@lahma

@lahma lahma commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

The SQL Server-backed integration suites now start their own database through Testcontainers for .NET. All they need is a running Docker daemon: no local SQL Server, no docker-compose, no hosts entry, no hand-run scripts. A new CI job runs them. This follows the Testcontainers setup that already works in Quartz.NET.

How it works

  • test/Shared/SqlServerTestEnvironment.cs is linked into each integration project.
    • An assembly-level [SetUpFixture] starts one mcr.microsoft.com/mssql/server:2022-CU27-ubuntu-22.04 container per test assembly, with a random port, so projects can run in parallel.
    • It runs build-support/create-integration-test-databases-and-users.sql and the project's own Data/*_database.sql, splitting on GO and using the same SqlClient path for every script.
    • It publishes SPRING_TEST_SQLSERVER_HOST, _USER and _PASSWORD, plus SPRING_TEST_SQLSERVER_CS_<DATABASE> for each database.
    • If startup fails it disposes the container and rethrows the original error.
  • The XML contexts resolve ${SPRING_TEST_SQLSERVER_CS_*} through VariablePlaceholderConfigurer / EnvironmentVariableSource, or through the existing PropertyPlaceholderConfigurers, where an environment variable is the fallback. C# tests call SqlServerTestEnvironment.GetConnectionString("Spring").
  • The four projects no longer hard-code SPRINGQA or (local) hosts.
  • Setting SPRING_TEST_SQLSERVER to an admin connection string runs the same, now idempotent, scripts against an existing server instead of a container. In that mode the build runs one test module at a time, because Microsoft.Testing.Platform otherwise runs the net472 and net10.0 modules at once.
  • Only the four test projects reference Testcontainers.MsSql 4.16.0.

Projects

Project Passed / skipped (both TFMs)
Spring.Data.Integration.Tests 69 / 31
Spring.Data.NHibernate5.Integration.Tests 22 / 26
Spring.Data.NHibernate5.NestedTxSuspension.Integration.Tests (net472) 2 / 1
Spring.Scheduling.Quartz3.Integration.Tests 2 / 0

Rot fixed along the way

  • A formatter had corrupted every Data integration _database.sql script and the NHibernate5 SQL scripts, splitting identifiers across lines ([\n decimal]), so SQL Server rejected them. These suites could not have passed for years.
    • The scripts are rewritten with the same tables, columns, types, keys and stored procedures; the review checked them against the originals.
    • They are UTF-8 now instead of UTF-16.
  • The Quartz 3.x SQL Server schema is copied unchanged from Quartz.NET v3.22.9 (Apache 2.0) into the Quartz3 project's Data/.
  • Whitespace inside two mapping-resource values broke resource lookup.
  • Two AppVeyor-era [Ignore]/[Explicit] attributes are removed.
  • The Quartz job-store test only slept for 20 seconds. It now asserts the job actually ran.
  • Five tests need MSDTC (distributed transactions), which can't work against a container. They are [Explicit("Requires MSDTC: …")]:
    • DTCTests and DTCTestsNoInterfaces .DeclarativeWithAttributes;
    • MultipleDbTests.MultipleDBAccess and .MultipleDBAccessUsingMultipleSessionScopes;
    • the nested-suspension NotSupported test.

Build and CI

  • New Fallout target IntegrationTest runs just those four projects, with TRX output. Off Windows it runs net10.0 only and skips the net472-only project. It works locally as well as on CI.
  • Test --test-integration-data now includes them. Before, the flag had no effect without --test-full. --test-integration-nms now matches the real Spring.Messaging.Nms.Integration.Tests name.
  • New integration-linux job in ci.yml (ubuntu-latest, 30 min) runs ./build.sh Compile IntegrationTest and always uploads artifacts/TestResults. publish.yml is untouched.
  • Deleted docker-compose.yml and the unused docker-compose-ci.yml. README "Running tests" and AGENTS.md "Integration tests" describe the new recipe.

Not in this PR: NMS/ActiveMQ integration on Testcontainers comes next. MSMQ tests stay Windows-only. Spring.Web.Conversation.NHibernate5.Tests uses SQLite and needs no container.

Verification

  • ./build.cmd Compile IntegrationTest passes locally, net10.0 and net472.
  • Ran twice against one server through SPRING_TEST_SQLSERVER (14 database setups on the same server), green both times.
  • ./build.cmd Test passes. The only failure is FormatUsingDefaults, which depends on this machine's regional settings.
  • spring-reviewer accepted it with follow-ups, which are in the last commit.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk

lahma added 7 commits November 4, 2026 00:31
Adds the shared SqlServerTestEnvironment (a linked source file) that starts a pinned SQL Server 2022 container, or uses the server named by SPRING_TEST_SQLSERVER, runs the database scripts and publishes SPRING_TEST_SQLSERVER_* environment variables. Spring.Data.Integration.Tests reads its connection strings from them instead of the hard-coded SPRINGQA host. The _database.sql scripts were mangled by a formatter (identifiers split across lines) and are rewritten. The two MSDTC tests are Explicit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
…Testcontainers

Connection strings come from the SPRING_TEST_SQLSERVER_* environment variables. The mangled creditdebit.sql, testObject.sql and RecreateDatabases.sql are replaced by one clean script that also creates the springqa2 login. DbProviderTemplateTests.UserCredentialsDbProvider runs again (its mapping resource name had trailing whitespace). MultipleDbTests.MultipleDBAccess is Explicit (MSDTC), the other test of the fixture is ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
…on SQL Server with Testcontainers

The App.config connection string is the SPRING_TEST_SQLSERVER_CS_SPRING environment variable (PropertyPlaceholderConfigurer, EnvironmentVariableMode Fallback). The mapping resource name had leading whitespace and is fixed. CanSuspendTransactionOnNotSupportedWithNestedRequired is Explicit (MSDTC).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
…h Testcontainers

Creates a quartz database with the Quartz 3.22.9 SQL Server tables (vendored tables_sqlServer.sql), resolves the connection string from SPRING_TEST_SQLSERVER_CS_QUARTZ, and replaces the 20 second sleep in the formerly Explicit test with an assertion that the scheduler ran the job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
…s, update the docs

IntegrationTest runs the four Docker-backed SQL Server projects with TRX reporting (net10.0 off Windows, both TFMs on Windows). Test --test-integration-data now includes those projects (the .Integration filter used to run first and exclude them), and --test-integration-nms matches Spring.Messaging.Nms.Integration.Tests. CI gets an integration-linux job. docker-compose.yml and docker-compose-ci.yml are removed; README and AGENTS.md describe the Testcontainers recipe and the SPRING_TEST_SQLSERVER override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
Produced by running the build, not edited by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
Run the modules of a project one at a time when SPRING_TEST_SQLSERVER is set (--max-parallel-test-modules 1), because Microsoft.Testing.Platform otherwise starts the net10.0 and net472 builds together and they recreate the same databases under each other. Drop the quartz database before creating it. MultipleDbTests.MultipleDBAccessUsingMultipleSessionScopes is Explicit (MSDTC), not Ignore. Keep the setup exception when disposing the container also throws. Upload the TRX with always(). Convert the Data scripts to UTF-8, drop the redundant EnvironmentVariableMode lines, and tighten the README override section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01LGkLQuAuUjvRjrU1AcTsmk
Signed-off-by: Marko Lahma <marko.lahma@gmail.com>
@lahma
lahma merged commit 8f2406c into main Nov 3, 2026
4 checks passed
@lahma
lahma deleted the 4x/testcontainers-integration branch November 3, 2026 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant