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

My main() Method is Better Than Yours

09.01.2008
| 6304 views |
  • submit to reddit

People are good at turning concrete examples into generalization. The other way around, it does not work so well. So when I write about general concepts it is hard for people to know how to translate the general concept into concrete code. To remedy this I will try to show few examples of how to build a web application from ground up. But I can’t fit all of that into a single blog post … So lets get started at the beginning…

Here is what your main method should look like (no matter how complex your application) if you are using GUICE: (src)

public static void main(String[] args)
  throws Exception {
    // Creation Phase
    Injector injector = Guice.createInjector(
             new CalculatorServerModule(args));
    Server server = injector.getInstance(Server.class);
    // Run Phase
    server.start();
}

Or if you want to do manual dependency injection: (src)

public static void main(String[] args)
  throws Exception {
    // Creation Phase
    Server server = new ServerFactory(args)
                               .createServer();
    // Run Phase
    server.start();
}

The truth is I don’t know how to test the main method. The main method is static and as a result there are no places where we can inject test-doubles. (I know we can fight static with static, but we already said that global state is bad here, here and here). The reason we can’t test this is that the moment you execute the main method the whole application runs, and that is not what we want and there is nothing we can do to prevent that.

But the method is so short that I don’t bother testing it since it has some really cool properties:

  1. Notice how the creation-phase contains the code which builds the object graph of the application. The last line runs the application. The separation is very important. We can test the ServerFactory in isolation. Passing it different arguments and than asserting that the correct object graph got built. But, in order to do that the Factory class should do nothing but object graph construction. The object constructors better do nothing but field assignments. No reading of files, starting of threads, or any other work which would cause problems in unit-test. All we do is simply instantiate some graph of objects. The graph construction is controlled by the command line arguments which we passed into the constructor. So we can test creation-phase in isolation with unit-test. (Same applies for GUICE example)
  2. The last line gets the application running. Here is where you can do all of your fun threads, file IO etc code. However, because the application is build from lots of objects collaborating together it is easy to test each object in isolation. In test I just instantiate the Server and pass in some test doubles in the constructor to mock out the not so interesting/hard to test code.

As you can see we have a clear separation of the object graph construction responsibility from the application logic code. If you were to examine the code in more detail you would find that all of the new operators have migrated from the run-phase  to creation-phase (See How to Think About the “new” Operator) And that is very important. New operator in application code is enemy of testing, but new in tests and factories is your friend. (The reason is that in tests we want to use test-doubles which are usually a subclass or an implementation of the parent class. If application code calls new than you can never replace that new with a subclass or different implementation.) The key is that the object creation responsibility and the the application code are two different responsibilities and they should not be mixed. Especially in the main method!

A good way to think about this is that you want to design your application such that you can control the application behavior by controlling the way you wire the objects together (Object collaborator graph). Whether you wire in a InMemory, File or Database repository, PopServer or IMAPServer, LDAP or file based authentication. All these different behaviors should manifest themselves as different object graphs. The knowledge of how to wire the objects together should be stored in your factory class. If you want to prevent something from running in a test, you don’t place an if statement in front of it. Instead you wire up a different graph of objects. You wire NullAthenticator in place of LDAPAuthenticator. Wiring your objects differently is how the tests determines what gets run and what gets mocked out. This is why it is important for the tests to have control of the new operators (or putting it differently the application code does not have the new operators). This is why we don’t know how to test the main method. Main method is static and hence procedural. I don’t know how to test procedural code since there is nothing to wire differently. I can’t wire the call graph different in procedural world to prevent things from executing, the call graph is determined at compile time.

In my experience that main method usually is some of the scariest code I have seen. Full of singleton initialization and threads. Completely untestable. What you want is that each object simply declares its dependencies in its constructor. (Here is the list of things I need to know about) Then when you start to write the Factory it will practically write itself. You simply try to new the object you need to return, which declares its dependencies, you in turn try to new those dependencies, etc… If there are some singletons you just have to make sure that you call the new operator only once. But more on factories in our next blog post…

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

Jess Holle replied on Mon, 2008/09/01 - 8:19am

It is curious that many "completely untestable" statements simply ignore better testing libraries.

EasyMock and its generation of mocking libraries have severe limitations.  Just because something is untestable with this generation of libraries does not mean it is untestable -- it means you're using antique testing approaches.

Try out something like JMockit instead where you can readily mock things EasyMock, etc, cannot touch before labeling code as untestable.

Ronald Miura replied on Mon, 2008/09/01 - 12:14pm

The main() method is 'completely untestable', period. It is a static method, called magically by the JVM, so, there's no easy way to: 1. know the dependencies so that you can mock them; 2. even if you know, you how would mock singletons and local variables (since there is no 'context' yet)?

Because of that, it should be kept minimal. Ideally, it wouldn't contain any logic but initialization of a true controller (and maybe parsing command-line parameters, delegating the logic to another object, of course).

What I'm trying to say is that the problem is not to find some hackish way to mock dependencies into the main() method (not Main class), but that it shouldn't need to be tested.

What people often do wrong is to start by writing the main method, as it was a script, and never refactor the logic to make it testable. As a result, what is left behind are those cryptic, untestable chunks of code the guy above probably is referring to.

Jess Holle replied on Mon, 2008/09/01 - 12:25pm

I'd concur that mocking is entirely a measure of last resort.  One should tests against stable interface boundaries -- not ripping every trivial bit of code apart and testing it as a unit with mocks aplenty just because one can.

That said, main() is quite testable.  Tests can just call it as any other method.  The only trick is to mock out System.exit() so you don't actually exit, but that's doable with JMockit, for instance.  If you want to test lower-level interface boundaries, then certainly these should be factored out -- but because they are significant components worthy of testing.  If they're not, then factoring them out into beautific interfaces that are nicely mockable, etc, is just a waste of time.

Comment viewing options

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