--- title: How Outside-In TDD Can Lead To More Modular Code disqus: pierodibello --- ###### tags: `TDD` # How Outside-In TDD Can Lead To More Modular Code This video https://www.youtube.com/watch?v=rW0ZFhKGdoM {%youtube rW0ZFhKGdoM %} The Jason's Guitar Shack kata: https://codemanship.wordpress.com/2020/10/10/the-jasons-guitar-shack-kata-part-i-core-logic/ Repo for the java version: https://github.com/jasongorman/guitar_shack_java/ We have to develop an app that sends alerts when products needs restocking. ### Outside-in, starting from the outermost entry point That style uses **stubs and mocks as placeholders for other parts of the design**: that allows me to work outside-in, from the entry point to the logic, with a **"fake it until I make it"** approach for other parts of the system that I don't want to focus on, **so I can focus on the solving one problem at a time**. He starts from the `StockMonitor`, the outermost part of our system, the entry point... In order to test-drive the `StockMonitor`, he stubs `ReorderLevel` and `Warehouse`, and mocks the `Alert` (because he needs to spy into it to check that the alarm has been sent). Then he moves inward to test-drive the implementation of real `LeadTimeReorderLevel`, faking its collaborator `SalesRate`. Then again he moves inward to test-drive the implementation of a real `ThirtyDayAverageSalesRate`. At the end he codes the real implementations of `RecentSales` and `Warehouse` (`SalesData` and `ProductData`) which actually talks to external systems to fetch historical sales data and products stock data. And so on, mocking and stubbing all the way down, * **Working inwards from the outside one layer of design at a time** * **Using test doubles so that we can solve one problem at a time.** `SalesData` and `ProductData` both uses a `Client`, which has a concrete implementation as a `RESTClient`, which uses a Java-provided HTTP client. A side effect of this outside-in approach, using test doubles to solve one problem at a time, is that the resulting code is **highly modular**: * Each class does one thing. * Most of the **dependencies are swappable** by dependency injection => loose coupling. * All modules are **hiding their internal details** (excluding `Product`, which is a data class retrieved from the web...). * Each interface has one method on it - doing one thing only, a single easy contract - tell don't ask - faking it when test-driving the upper layer which uses it, deferring its implementation ### Code coverage * high unit test coverage: 91% line coverage with unit tests, the `Web` class is not unit tested * 100% line code coverage with all the test suite, including integration tests. * the integration test covers 90% line coverage, because it's an **"happy path"** scenario ### Integration test (or should we say "acceptance test"?) **The purpose of the integration tests is to touch as many parts of the code as possible**, to check that when we plug all the concrete stuff together, it's working as expected in a "happy path" scenario. The integration test was written **after the fact**, and it was green at the first launch (sort of acceptance test?). In the integration test is clear the composition of the whole app like the "Russian Dolls". ![](https://i.imgur.com/ejtBNgh.png) **Swappability** of components is a key benefit of this test-driven approach => it brings testability and flexibility (DIP, OCP). This outside-in TDD style, with stubs and mocks as placeholder to allow you to make one thing at a time, can lead to highly modular design and highly testable. Also, this code is flexible, because of its modularity and composition. ### Contract tests `SalesDataTestBase` has a contract test version and a unit test version. I would call them "integration test", but I understand its role here is to check the contract between our system and external data providers (as in https://martinfowler.com/bliki/ContractTest.html). The unit test can be green but the system not working because of a change in the contract! Those contract tests help me here. Unit test green, contract test red => warning, maybe something changed on the APIs? Those tests are useful for my dev team but also for the data providers' teams: they can run my contract tests to check that the expectations clients are making are satisfied by their services' API. ## Doubts / observations * is `SalesDataUnitTest` (and `ProductDataUnitTest` BTW) a true *unit test*? Sure enough, it's fast, quite narrow-focused, and almost focusing on a single behavior. The "quite" and "almost" in my previous statement express my perplexity here: this test actually is also "testing" the real `RESTClient` with a fake JSON to deserialize using an external library (GSon). * The `RESTClient` was refactored to extract away in other classes the lowest policies: * `Network`, whose implementation (`Web`) actually executes an HTTP call * `RequestBuilder`, a concrete class to create an HTTP request (not unit tested, but tested indirectly in other unit tests) * The integration test uses a mock `Alarm` because he's not still implemented a *real* alarm service to send real notifications. * He uses the integration test, `StockMonitorIntegrationTest`, as a sort of an "acceptance test", written at the end of the development to check that everything works together.