Writing Testable Systems
Complex enterprise applications are generally hard to maintain, and risky and difficult to change. As a new developer on a team, a large legacy code base is often difficult to understand, especially when the code has evolved over a long period and new functionality has been grafted onto an existing application. The situation is exacerbated by the usual time and delivery pressures of commercial enterprise systems development. With a modest amount of effort, however, it is possible to design and develop applications that are well architected and easy to change, and moreover that remain so as they grow and evolve.
What Is a Testable System?
There are certain characteristics that are common to all clean, well-architected applications. Features such as modular design, good separation of concerns, loose coupling, and high cohesion - behavior in one and only one place within an application - are all well-established best practices. Writing applications with testability in mind tends to lead to systems that display these characteristics. For instance, focusing on the ease of black- and white-box testing of individual modules is a sure-fire way to generate cleanly separated functional units. Saying that a module is easy to unit test is the same as saying we know exactly what the module should do, under what circumstances, and that we know (and can prove) its boundary behavior. Saying that a module is easy to integration test implies that it is easy to deploy and configure.
To keep ourselves honest and consistent, we write tests as we go to define and prove that components work in a particular way, and we automatically run all those tests often. As discussed in my previous article ("Test-Driven Development Is Not About Testing" [JDJ, Vol. 8, issue 11]), each test case becomes a model client of the module it is testing. This can be true at any level of granularity, whether the module is an individual class, a deployed component such as an EJB or servlet, a service, or an entire subsystem. The size of the unit may vary, but the purpose of the test case remains consistent.
This new column looks at what makes an application testable, and provides practical help and real-world advice for writing applications in a clean and test-driven fashion. Its focus will be on simplicity of design, from the viewpoint of a developer wanting to work with clean code, not as a professional tester. The target audience is enterprise application developers who are either embarking on a new application or maintaining an existing enterprise application. I'll offer pragmatic advice and techniques for choosing - and sticking to - clean and extensible architectures, and help you contrast different approaches effectively from a testability perspective.
What Is an Enterprise Application?
If you ask 10 developers what constitutes an enterprise application, you'll most likely get 10 different responses, but in general an enterprise application will display some, if not all, of the following characteristics:
It is these sorts of environments I will be discussing in this series. I'll look at various "typical" Java application models, such as Web apps, full-blown multitier J2EE applications, and event-driven architectures, and explore how strategies such as MVC and inversion of control can improve their design and structure. In addition to the typical n-tier enterprise architecture, we will look at message-driven applications, Jini, JavaSpaces, and various other nonclient/server models for distributed application development.
- Integration with existing legacy applications (often in different languages and on heterogeneous hardware and operating systems)
- Some sort of persistence, typically a relational database
- Multiple tiers, with at least presentation and business logic, and often data access broken out into separate tiers
- Many users, across many widely distributed sites, often in different countries
- High throughput and performance requirements
- High availability and resilience
Why Should It Matter to Me?
For notoriously hard-to-test components such as Swing widgets, EJBs, servlets, and database connections, we'll look at ways to test outside of the container and review tools and frameworks that help promote loose coupling and overall application goodness. I hope you'll find this new column pragmatic and directly applicable to your development world.
The first article in the series will present some alternatives to Pet Store-shaped enterprise applications, and subsequent articles will look at the testability of these architectures and enable you to create and evolve clean, maintainable enterprise systems using them.
About The Author
Dan North has been writing software for 12 years, and is a programmer and coach
for ThoughtWorks (www.thoughtworks.com), a software development consultancy, where he encourages people to write tests.