DE- Industries
- Finance
Nearshore software development for finance—secure, scalable, and compliant solutions for banking, payments, and APIs.
- Retail
Retail software development services—e-commerce, POS, logistics, and AI-driven personalization from nearshore engineering teams.
- Manufacturing
Nearshore manufacturing software development—ERP systems, IoT platforms, and automation tools to optimize industrial operations.
- Finance
- What we do
- Services
- Software modernization services
- Cloud solutions
- AI – Artificial intelligence
- Idea validation & Product development services
- Digital solutions
- Integration for digital ecosystems
- A11y – Accessibility
- QA – Test development
- Technologies
- Front-end
- Back-end
- DevOps & CI/CD
- Cloud
- Mobile
- Collaboration models
- Collaboration models
Explore collaboration models customized to your specific needs: Complete nearshoring teams, Local heroes from partners with the nearshoring team, or Mixed tech teams with partners.
- Way of work
Through close collaboration with your business, we create customized solutions aligned with your specific requirements, resulting in sustainable outcomes.
- Collaboration models
- Services
- About Us
- Who we are
We are a full-service nearshoring provider for digital software products, uniquely positioned as a high-quality partner with native-speaking local experts, perfectly aligned with your business needs.
- Meet our team
ProductDock’s experienced team proficient in modern technologies and tools, boasts 15 years of successful projects, collaborating with prominent companies.
- Why nearshoring
Elevate your business efficiently with our premium full-service software development services that blend nearshore and local expertise to support you throughout your digital product journey.
- Who we are
- Our work
- Career
- Life at ProductDock
We’re all about fostering teamwork, creativity, and empowerment within our team of over 120 incredibly talented experts in modern technologies.
- Open positions
Do you enjoy working on exciting projects and feel rewarded when those efforts are successful? If so, we’d like you to join our team.
- Hiring guide
How we choose our crew members? We think of you as a member of our crew. We are happy to share our process with you!
- Rookie boot camp internship
Start your IT journey with Rookie boot camp, our paid internship program where students and graduates build skills, gain confidence, and get real-world experience.
- Life at ProductDock
- Newsroom
- News
Stay engaged with our most recent updates and releases, ensuring you are always up-to-date with the latest developments in the dynamic world of ProductDock.
- Events
Expand your expertise through networking with like-minded individuals and engaging in knowledge-sharing sessions at our upcoming events.
- News
- Blog
- Get in touch
19. Feb 2026 •1 minute read
Spring Boot & Testcontainers
Bojan Ćorić
Sofware Engineer
Testcontainers is an open source library for providing throwaway, lightweight instances of anything that can run in a Docker container. Think of it as “Infrastructure-as-Code” specifically for your test suite. Instead of writing external scripts (like Bash or Docker Compose) to set up your environment before running tests, you define the environment inside your test code in your language of choice.
While it started in the Java world, Testcontainers is now a standardized protocol for testing across almost all major programming languages (Go, Python, C#, etc.).
The boilerplate era: Manual configuration
Before Spring Boot integrated tightly with Testcontainers, setting them up was cumbersome. Developers had to manually manage the container lifecycle and, more painfully, manually tell Spring Boot where to find these dynamically started containers.
// ❌ The verbose, manual way (avoid this now)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ManualConfigTest {
static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:18.1-alpine3.23");
@BeforeAll
static void startContainers() {
postgresContainer.start();
}
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresContainer::getUsername);
registry.add("spring.datasource.password", postgresContainer::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testDatabase() {
userRepository.save(new User(null, "John", "Doe", "john.doe@mail.com"));
List<User> users = userRepository.findAll();
assertThat(users).isNotEmpty();
}
}
JUnit support: @Testcontainers
The first major quality-of-life improvement came from the Testcontainers JUnit 5 support. By using the @Testcontainers and @Container annotations, the library took over the lifecycle management (starting and stopping containers).
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers // Enable testcontainers support
public class ManualConfigTest {
@Container // JUnit manages lifecycle
static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:18.1-alpine3.23");
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresContainer::getUsername);
registry.add("spring.datasource.password", postgresContainer::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testDatabase() {
userRepository.save(new User(null, "John", "Doe", "john.doe@mail.com"));
List<User> users = userRepository.findAll();
assertThat(users).isNotEmpty();
}
}
The modern way: @ServiceConnection
Spring Boot 3.1 introduced a game-changer: @ServiceConnection. Spring Boot now understands standard containers (Postgres, Mongo, Kafka, Redis, etc.). When you annotate a container with @ServiceConnection, Spring Boot automatically finds the connection details (URL, username, password, ports) and injects them into the application context.
No more property overrides. No more manual config.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
public class ManualConfigTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:18.1-alpine3.23");
@Autowired
private UserRepository userRepository;
@Test
void testDatabase() {
userRepository.save(new User(null, "John", "Doe", "john.doe@mail.com"));
List<User> users = userRepository.findAll();
assertThat(users).isNotEmpty();
}
}
Conclusion
Testcontainers has evolved from a niche library requiring lengthy setup to an absolute necessity for modern software development. It is no longer just a “nice-to-have”; it is the new standard for building reliable, production-grade applications. Testcontainers eliminates the “it works on my machine” and “it works with the mock” classes of bugs entirely.
Ready to implement this? You don’t have to start from scratch. I’ve created a GitHub repository featuring “real-world” setups for Postgres, Redis, and Kafka.
I also included a bonus section demonstrating how to manually configure containers when @ServiceConnection isn’t available, giving you full control over any service you need to containerize.
Clone the repo and start experimenting: https://github.com/Corke123/testcontainers-demo
Tags:Skip tags
Bojan Ćorić
Sofware EngineerBojan is a backend software developer at ProductDock, specializing in Java and Spring. He has been actively involved in developing robust and scalable backend systems for the past two years. His expertise in these technologies and his dedication to continuous learning make him a valuable asset in the field of software development.