Like all good designs, Ports-and-Adapters makes things more testable. Everything is tested in a tight edit/build/test cycle except for the "real" adapters. The "real" adapters don't change very much, so we test them on a slower cadence.
"Don't change very much" isn't very reassuring though. I don't think about the real adapters much, but I at least want something to tell me that my real adapters aren't changing. If they need to change, grab my attention so I can run the focused integration tests.
Arlo Belshee suggested record/reply tests. Here's an example, in C# with HTTP:
Record-and-passthrough integration testing
Test->HttpClient: HTTP Request HttpClient->Pass-through Recorder: HTTP Request note right of HttpClient: Record the request Pass-through Recorder->Some service: HTTP Request Some service->Pass-through Recorder: HTTP Response note right of HttpClient: Record the response Pass-through Recorder->HttpClient: HTTP Response HttpClient->Test: HTTP Request
While developing the adapter we run "focused integration tests", testing the adapter it against the real dependency. For each test we record the HTTP requests and responses.
Since these tests are slow/flaky/expensive we don't run them in the edit/build/test cycle, but only when actively working on the adapter.
Verify-and-replay isolated testing
Test->HttpClient: HTTP Request HttpClient->Player/Verifier: HTTP Request note right of Player/Verifier: Verify the request note right of Player/Verifier: Return the recorded the response Player/Verifier->HttpClient: HTTP Response HttpClient->Test: HTTP Request
While doing development on the rest of the system, or while refactoring in the real adapter, we run the real adapter against the recorded messages. This test tells us that the adapter's behavior hasn't changed (in any way that we test for), without the speed/reliability/cost of talking to the real service.
#NoMocks
This is not a mock. How so? And why not use a mock?A mock encodes what we know about the thing we're mocking. We write our understanding in code. If our understanding doesn't match the real service, our tests can pass but the system is will fail in production.
New requirements mean extending the mock. As the mock grows, it needs good design to keep from becoming unmaintainable. This recorder is cheap to extend: write a new test, run it, save the results.
Code
Loading Gist eb5b5fc548d253155ad113ba60898ba8....