
Integration Testing with Containers: Moving Beyond Mocks for Real-World Confidence with MSSQL, SMTP, and Azurite
Ever feel like you’re acing every practice session, only to choke when game time arrives? That’s exactly how integration testing can go if you rely solely on mocks.
That's why our team relies on containerized resources such as MSSQL, SMTP4DEV, and Azurite.
By running these services in Docker containers, we create an environment that mirrors production closely, catching those hidden bugs before they ever get a chance to surprise you in the real world.
Why we bother with Test Containers instead of Mocks?
Mocks can be handy because they let you fake external services. The problem is that mocks can miss important details.
For example, an email mock might confirm that you tried to send something, but it won't confirm that your messages have the correct format, attachments, or recipient addresses.
If your code thinks it's sending an email, but the actual email server rejects it, you could end up with a production crisis on your hands.
Test Containers give you the real thing in a controlled environment.
Having a suite of realistic tests, allows us to refactor our projects and update versions of .NET with confidence. Without a good suite of tests these changes are a real risk.
Using the .NET TestContainers Library
The .NET TestContainers library (Testcontainers for .NET) is the friendly assistant that helps you spin up actual Docker containers for services like MSSQL, SMTP4DEV, and Azurite. It hides the messy details of Docker commands, so you can focus on writing code. Whether you're on your local machine or using GitHub Actions in your pipeline, these containers provide a consistent, production-like experience.
Here’s a quick example of how you might spin up an MSSQL container in your tests:
Figure: In this snippet, the MsSqlBuilder from the .NET TestContainers library takes care of setting up your database container. Once it's running, you can interact with it just like you would with a normal MSSQL instance. When your tests finish, the container can shut down cleanly, leaving no mess behind. See the SSW.CleanArchitecture template for a full implementation - https://github.com/SSWConsulting/SSW.CleanArchitecture
Benefits of Testing with Containers
✅ Realistic Environments: Instead of pretending to have an MSSQL server or an SMTP service, you actually have them running in containers. This makes your tests behave like they do in the real world.
✅ Consistency Everywhere: Ever had that "it worked on my machine" moment? Test containers reduce the odds of that happening because the same Docker images run locally and in CI/CD. As long as Docker is available, you're good to go.
✅ Early Bug Detection: By hooking into actual services, you spot issues like transaction mishandling, email format errors, or file upload glitches long before production. Why wait for your users to find out something’s broken?
✅ No Overwhelming Complexity: Even though there's a lot going on under the hood, the .NET TestContainers library (Testcontainers for .NET) keeps it simple. You won’t have to fuss with Docker commands, environment variables, or container networking.
Practical Examples
MSSQL Test Container (https://dotnet.testcontainers.org/modules/mssql/)
- What It Is: A full MSSQL server running in a Docker container.
- Why It Helps: Each test can have its own isolated database, so you never have to worry about leftover data messing up your results. Tools like Respawn (jbogard/Respawn: Intelligent database cleaner for integration tests) make resetting the database quick and painless, so you get a clean slate every time.
SMTP4DEV Test Container (https://github.com/rnwood/smtp4dev)
- What It Is: A lightweight SMTP server in a container that's perfect for development.
- Why It Helps: Instead of a mock email service that only confirms an email was "sent," SMTP4DEV tests the actual content, recipients, and attachments of your emails. If there's an issue with your mail formatting, you'll find out fast.
Azurite Test Container (https://node.testcontainers.org/modules/azurite/)
- What It Is: A containerized emulator for Azure Blob Storage.
- Why It Helps: Lets you test file uploads, downloads, and metadata in ways that feel just like using the real Azure service. We once ran into a mismatch between our SDK version and Azurite, but we fixed it by passing a simple flag. Little hiccups like this remind us how similar it is to real-world scenarios. Without Azurite, you’d have to connect to a blob storage account running in Azure.
Hands-On Experiments
If you want to get a feel for how this works, try setting up a simple proof of concept.
Write a tiny .NET application that sends an email, stores a file in Azure Blob Storage, and writes some data to MSSQL.
Then create a test project that spins up these three containers, runs your code, and checks the results. You’ll immediately see the power of having production-like services at your fingertips.
The Broader Impact on Your Project
- Production-Like Testing: When your test environment mirrors production, you're more likely to find odd issues before they blow up in your face.
- Speed and Reliability: After the initial container startup, Docker caches the images, so your tests usually run pretty quickly. You can even run tests in parallel with minimal fuss.
- Easy Scaling: If your architecture grows to include more services, just spin up more containers. You can test complex interactions between multiple modules without getting lost in a web of mocking frameworks.
Containerized integration testing is more than just a fancy way to run tests. It’s a practical method for bridging the gap between your development environment and the realities of production. By using real MSSQL, real SMTP, and real Azure Blob Storage (through Azurite), you catch critical issues earlier and build confidence in your code. The .NET TestContainers library streamlines this process, giving you hands-on experience without drowning you in Docker details.
If you're tired of debugging issues that only appear in production, give containerized testing a spin. It will help you find problems sooner, reduce production surprises, and give you the peace of mind to keep innovating. And hey, if you discover you’ve been sending out blank emails, you’ll be glad you caught it before your customers did. Happy testing!
Talk to us about your project
Connect with our Account Managers to discuss how we can help.