The case for network unit tests
Should IT start thinking like software engineers?
Unit tests are a foundational concept in software engineering, designed to automatically verify that small, individual pieces of code (called "units") behave as expected. Typically, a unit test exercises a single function, method, or module in isolation, providing it with specific inputs and checking that the outputs match the expected results.
The main goals of unit testing are:
- Catch bugs early: By testing code in isolation, developers can quickly identify and fix issues before they propagate into larger, more complex systems.
- Enable safe refactoring: With a robust suite of unit tests, engineers can confidently make changes to code, knowing that any unintended side effects will be caught by failing tests.
- Document intended behavior: Unit tests serve as living documentation, showing how code is supposed to work and what edge cases should be handled.
In practice, unit tests are typically run automatically as part of a continuous integration (CI) pipeline, ensuring that new changes don't break existing functionality. This approach has become a best practice in modern software development, leading to more reliable, maintainable, and robust systems.
Dont boil the ocean
Unit tests that focus on individual components are powerful because they isolate specific pieces of functionality, allowing you to verify that each part of your system behaves exactly as intended. By testing components in isolation, you eliminate external variables and dependencies that could mask bugs or introduce false positives/negatives. This targeted approach ensures that when a test fails, you know precisely which part of the system is responsible, making debugging faster and more effective.
Moreover, focusing on individual units means you are explicitly defining the expected behavior for each component. This not only helps catch subtle bugs early but also prevents regressions when changes are made elsewhere in the system. In essence, unit tests act as a safety net, guaranteeing that the core building blocks of your system remain reliable, regardless of how the surrounding environment evolves.
For networks, this philosophy translates to testing each control, rule, or segment independently—so you can be confident that every intended restriction or allowance is actually enforced, and that your security posture is built on verified, not assumed, foundations.
To put it another way, you shouldn't need to find a vulnerability, exploit it, and be able to bypass EDR just to test your network egress rules.
Testing automatically
Being able to test automatically makes testing less of a chore and more likely to find problems. In software, automated unit tests run every time you make a change, catching regressions and unexpected side effects before they reach production. This same principle can—and should—be applied to your network.
Imagine if, every time you updated a firewall rule, spun up a new server, or changed a routing policy, a suite of automated tests immediately checked that your critical egress controls were still working as intended. Instead of relying on manual spot checks or waiting for a security incident, you’d get instant feedback: “Port 443 is open as expected, but port 53 is unexpectedly reachable from this subnet.”
Automated network tests can be scheduled to run regularly (daily, weekly, or after every change), and can even be integrated into your infrastructure-as-code or CI/CD pipelines. This approach transforms network validation from a tedious, error-prone manual process into a reliable, repeatable safety net—just like unit tests do for software.
By making network testing automatic, you not only reduce the burden on your team, but you also dramatically increase the chances of catching misconfigurations and egress paths before they can be exploited.
Finish the test
Testing complicated environments is difficult, and testing secure environments is even harder. The temptation is often to try to test everything at once, but this quickly becomes overwhelming and unsustainable. Instead, focus on breaking down your network into smaller, manageable pieces—just like you would with software unit tests.
Start by identifying your most critical controls or the riskiest egress paths. Write simple, targeted tests that verify whether those specific controls are working as intended. For example, can a server in your DMZ reach the internet over DNS when it shouldn't? Can a developer workstation connect to an unexpected outbound port? Each of these scenarios can be tested in isolation, giving you clear, actionable feedback.
Automate what you can. Even a handful of automated network tests, run on a schedule or triggered by infrastructure changes, will catch more issues than sporadic manual checks. Over time, you can expand your test suite as your environment and threat model evolve.
The key is to start small, iterate, and build a habit of continuous validation. You don't need to boil the ocean—just make sure the most important parts of your network are being tested, and let automation do the heavy lifting. This approach will help you catch misconfigurations early, reduce risk, and build a more resilient security posture—one test at a time.