Skip to content

Rethinking software tests: grouping by IO vs Not IO in Hexagonal Architecture

Published: at 11:00 AM
Author: Leonardo Filippelli

Testing is one of the cornerstones of software development. It ensures that our code behaves as expected, helps prevent bugs, and ultimately contributes to the reliability of our systems. Traditionally, tests are categorized into two broad groups: unit tests and integration tests. However, this categorization doesn’t always capture the full complexity of the testing landscape and sometimes lead to confusion because not everyone understand the same or have the same definition for those concepts. We’ll explore a simpler and more effective approach inspired by this article.

1. Test Categories: IO vs. Not IO Tests

IO Tests

IO Tests are those that interact with external systems or resources. These could include interactions with:

These tests are often necessary to verify that your application works as expected when dealing with external dependencies. However, they tend to be more fragile and slower, as they depend on the availability and state of external systems. Additionally, network or database failures can cause false negatives in your test results.

Not IO Tests

Not IO Tests focus on testing the core logic of your application without relying on external systems. These tests validate:

These tests run faster and are generally more reliable, as they don’t depend on external services or resources. They focus on ensuring that the internal parts of your application are correct, which leads to more stable tests that provide quicker feedback.

2. Hexagonal Architecture and Testing

What is Hexagonal Architecture?

Hexagonal Architecture, also known as Ports and Adapters, is a design pattern that helps isolate the core logic of your application from external dependencies. In this architecture:

By separating the core from external dependencies, Hexagonal Architecture allows you to maintain flexibility in how your application interacts with its environment. This design pattern also plays a crucial role in testing strategies, as it enables you to isolate your core logic and use test doubles for external resources during testing.

Testing in the context of Hexagonal Architecture

In a Hexagonal Architecture, tests can be categorized as either Not IO Tests or IO Tests based on the level of interaction with external systems.

For example, if you’re testing a service that performs business logic calculations without needing to interact with a database, that’s a Not IO Test. If that service has external dependencies but you use test doubles (mocks or fakes) for them, those tests are still Not IO Tests. On the other hand, if you’re testing a service that retrieves data from an external API or database, that’s an IO Test.

3. Advantages of grouping tests by IO vs. Not IO

Faster Feedback

Not IO Tests generally run faster because they don’t rely on external systems. By focusing on the core business logic, these tests can be run frequently during development to provide rapid feedback to developers.

Isolation

By categorizing tests based on IO, you can better isolate your business logic from external dependencies. This isolation allows you to test the behavior of your application without worrying about issues like network latency or database connectivity.

Resilience

While IO Tests can be more prone to failure due to dependencies on external systems, using test doubles (like mocks or fakes) can help isolate the external systems during testing. For example, you can mock database interactions or external API calls to ensure your tests run reliably even if the actual services are unavailable.

4. Conclusion

Grouping tests by IO and Not IO categories allows for a more refined approach to testing in software development. By using this classification, you can ensure that your core logic is well-tested in isolation, while also ensuring that your application works as expected when interacting with external resources.

In the context of Hexagonal Architecture, this approach is particularly powerful, as it allows you to separate your core business logic from infrastructure concerns. This separation leads to faster, more reliable tests that can be executed frequently, ensuring your application is both robust and resilient.

Ultimately, adopting this classification strategy, along with careful use of test doubles, can enhance your overall testing strategy, leading to better software quality and more maintainable code.


Next Post
Semantic Search 101