Chapter 9. Configurability

In this chapter, we will learn how to configure applications using simple, robust tools based on environment variables. We'll learn about configurability and its importance to testability. We'll deal with real-world configuration concerns that come up during development and testing. We'll address these concerns in a way that makes testing easy but addresses the safety and security requirements of a production environment.

Although we won't directly use RSpec at all in this chapter, its contents are the most important in this book. That is because proper configuration management is fundamental to testability and is something that is often done wrong. You can learn about RSpec easily from other sources, but configurability is one of those real-world concerns that is difficult to learn except the hard way: though direct experience, making mistakes, and learning from them through trial and error.

Here is what we will cover in this chapter:

  • Configuration via environment variables
  • Loading environment variables from a file
  • Overriding configurations for testing
  • Making configurations easily accessible for inspection
  • Managing sensitive configuration values
  • Ensuring required configurations are set at start time

Configuration and testability

In our test and application code throughout this book, we've seen many examples of configurations set by environment variables. Here are some examples to refresh our memory:

  • In Chapter 3, Taking Control of State with Doubles and Hooks and Chapter 5, Simulating External Services, Redis was configured using ENV['WQ_REDIS_URL']
  • In Chapter 5, Simulating External Services, network connectivity in specs was set with ENV['ALLOW_NET_CONNECT']
  • In Chapter 6, Driving a Web Browser with Capybara and Chapter 7, Building an App from the Outside In with Behavior-Driven Development, the browser used to run e2e tests with Capybara was set with ENV['BROWSER']
  • In Chapter 8, Tackling the Challenges of End-to-end Testing, the Sinatra environments and authentication secret were set using ENV['SINATRA_ENV'] and ENV['JWT_SECRET']

Using environment variables in this way gave us powerful options when running our app and its tests while maintaining a clean separation between code, tests, and configuration. Environment variables are a commonly used mechanism to configure applications. They are simple, portable, and flexible.

What is so important about configuration?

It turns out that real-world apps require a high degree of configurability to be practical. During development, testing, and deployments to various types of environment, the app must be configured slightly differently. We simply can't develop our app or run tests without some ability to configure certain features.

Once we have a number of different configuration options, the danger arises of confusion and misconfiguration. We may forget to set an important configuration that is required for correct functioning, for example, a database URL configuration, without which the application cannot even start. We may have default values that are dangerous when used in certain environments. For example, if the default value for the database connection URL was our production server, we may wipe out all of our app's data in the production environment by running our local unit tests.

What does all this have to do with testing?

A reliable set of tests is impossible without a high level of configurability in code and tests. Tests will fail apparently randomly on different systems due to hidden dependencies on configurations that may only work on certain machines or on certain networks. Tests will pass although the code is not working properly due to misconfiguration. Worst of all, these types of issues are very difficult to detect, diagnose, and fix. A system with bad configurability is very hard to fix, as code that has not been cleanly separated from configuration will require basic rewrites to become configurable. Such basic rewrites are costly and inevitably introduce bugs. Therefore, we have to consider configurability from the beginning if we are to achieve testability.

All of the examples we've worked with have had a decent amount of configurability, although the subject has not been a primary focus until now. However, we can do much better. To address the need for configuration that is flexible and manageable, we'll need to put some effort into the configuration system. One important consideration is to keep configuration minimal. Too many configuration options, with some interacting together, will lead to confusion, regardless of how many tools we build to manage them.

Environment variables allow us to set simple string variables when we start a process. We can change a configuration simply by changing the command used to start the process. We've seen many examples of this throughout the book. For example, we could run our specs with the Firefox browser using BROWSER=firefox rspec and with the Chrome browser using BROWSER=chrome rspec. Within our Ruby code, we can look up the value of the configuration by accessing the Hash-like ENV global variable, for example, ENV['BROWSER'].

As our application grows, so will the number of configurations. Specifying each of these on the command line will be burdensome and prone to error. We'll need to organize our configurations somehow. There are many approaches to this, some of which are quite sophisticated. We'll learn about a simple, file-based approach, which can be used on its own just fine in large, real-world apps, or combined with more complex tools as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset