As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

Pass Around Ginormous Context Objects

10.29.2008
| 4496 views |
  • submit to reddit

This question was submitted by one of the readers, and it shows a common questions people ask when they are coming up to speed when trying to write testable code….

About “Pass[ing] around ginormous context objects” (http://misko.hevery.com/2008/07/24/how-to-write-3v1l-untestable-code/) – this situation comes up for me when [I] trying to get configuration settings into my application. I want objects deeper in the system to have some configurable values which come from outside the system, and I don’t want everyone along the way to know about what those configurable values are. How do you deal with this case? I’m thinking about a collection of console utilities which take settings like number of retries for different actions, timeouts to allow, logging levels, and other things. Some settings will be of interest to many components and others only to one, and it seems like if I decompose them then whomever handles the construction will have to know a lot about what settings classes know about and don’t know about.

// I would hate the class constructing the
// FileCopier to know it is interested in
// all of these!
FileCopier copier = new FileCopier(logger,
           copierSettings, generalTimeouts);

// I end up doing this:
FileCopier copier = new FileCopier(configuration);
// and inside..
FileCopier(Configuration configuration) {
  X = configuration.GetSetting(“X”, DefaultValue);
  …
}

Lets look into each of these in turn:

  1. “I would hate the class constructing the FileCopier to know it is interested in all of these!” Why is this a problem? as discussed in ‘Breaking the Law of Demeter is Like Looking for a Needle in the Haystack‘ When you ask for things explicitly it makes it easy to pass in test-doubles for these dependencies and hence it makes the code testable. Not only is the code testable it is easy to understand. If I am new to the code-base I look at the constructor and I see that logger, copierSettings and timeouts are things which I may be interested in. On the other hand passing in a Context object it is hard for me to know which parts from the Context object the class-under-test is interested in. It also makes testing harder. Now I have to create Mock Context and override all of the getLogger(), getCoppierSettings() and so on for these methods to return the real test-doubles of interest. But How do I know what needs to be mocked out? I need to read the code, I can’t just look at the constructor and know. But there is more: Suppose I want to reuse FileCopier class in a different project. In that case I need to reuse all of the compile-time dependant classes as well. In this case it is the Context. But context is a kitchen sink and it knows about everything in your application which means I have to resuse those objects as well. So the code with Context objects is not reusable/re-compilable.
  2. The place where our mental model diverged is in this sentence: “I don’t want everyone along the way to know about what those configurable values are.” The fear is that in order to call the constructor the caller will have to know about the parameters, and its caller will have to know and so on. We talked about this in ‘Dependency Injection Myth: Reference Passing‘. However, we are making one more mistake. We are mixing object creation (and look up) with application logic, see: ‘How to Think About the “new” Operator with Respect to Unit Testing‘. The basic mistake people make at this point is that they assume that the object construction graph is same as object instantiation graph. This assumption is mostly true only if you sprinkle new-operators with your application logic. If the new operator is removed from application logic than your application has two phases. Phase (1) wire the objects together in order to instantiate the application. In Phase (1) all we do is call the new operators and pass in the right dependencies. We do not call any methods! In phase (2) we execute the application, here we stay away from the new-operators and call methods so that the objects can collaborate. Once the application is constructed we no longer need to pass the configuration information around. We simply execute methods on FileCopier.
  3. Loging is a Singleton and we have discussed Singletons here, here and here. The root problem is that Singleton (the design pattern) is the global state and global state sucks, from testability, maintainability, and few other -bilities. So should we pass in the Logger through the constructor. Well that depends… If you want to assert that under some conditions a specific log message is produced than you have no choice but to pass it in. On the other hand, if you just want logging, and don’t want to test that it works correctly, than you can do Logger.getLogger(Class.class). This does not make Singletons OK, but it is benign-kind-of-evil. The reason is that with Logger the information only flows one way: Into the Logger. Your application does not read data from the Logger and more importantly does not behave any differently depending on if the Logger is turned-on or turned-off (It behaves different only in the sense that things don’t get printed, but not in the sense that your application now computes a different answer.) Most Singletons do not fall into this rare category where we push data only in, and hence most Singletons are a nightmare to test.

NOTE: when I say Singleton I mean the design anti-pattern with a global instance field as in private static Singleton instance = new Singleton(); This is what makes the Singleton a global. To be contrasted with singleton (lower case ’s’) as in a single instance of something without a global instance field.

From http://misko.hevery.com

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Guillaume Jeudy replied on Wed, 2008/10/29 - 8:15am

Nice post Michael. I've been reading most of your posts very interesting stuff, it reinforces my own conviction and quest towards writing easily testable code.

 In my previous project we were not using a DI container and our classes depended on humongous context objects because we had no clear instanciation strategy. That is the best that we could do at the time to somehow make our code testable.

 Now i'm using Spring and Seam and these things became alot easier. Spring is a must-have to help produce good testable code. The only thing I find Spring doesn't handle so well are the scopes and you often get into scope impedance mismatch, you have to resort to AOP and proxy magic to handle instantiation of anything other than singletons. I found Seam to be superior in this area where bijection concepts supports a stateful programming model better.

 Keep up the good work.

Joe Farmer replied on Thu, 2008/10/30 - 9:44am

Sometimes you have objects you have to instatiate on demand within your business logic. Do it with factories. And Logger.getLogger(Class.class) is ugly. Make your logger a component with interface and pass around.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.