By Miško Hevery
- Unit Testing as the name implies asks you to test a Class (Unit) in isolation.
- If your code mixes Object Construction with Logic you will never be able to achieve isolation.
- In order to unit-test you need to separate object graph construction from the application logic into two different classes
- The end goal is to have either: classes with logic OR classes with “new” operators.
Unit-Testing as the name implies is testing of a Unit (most likely a Class) in isolation. Suppose you have a class House. In your JUnit test-case you simply instantiate the class House, set it to a particular state, call the method you want to test and then assert that the class’ final state is what you would expect. Simple stuff really…
class House {
private boolean isLocked;
private boolean isLocked() {
return isLocked;
}
private boolean lock() {
isLocked = true;
}
}
If you look at House closely you will realize that this class is a leaf of your application. By leaf I mean that it is the end of the dependency graph, it does not reference any other classes. As such all leafs of any code base are easy to tests, because the dependency graph ends with the leaf. But testing classes which are not leafs can be a problem because we may not be able to instantiate the class in isolation.
class House {
private final Kitchen kitchen = new Kitchen();
private boolean isLocked;
private boolean isLocked() {
return isLocked;
}
private boolean lock() {
kitchen.lock();
isLocked = true;
}
}
In this updated version of House it is not possible to instantiate House without the Kitchen. The reason for this is that the new operator of Kitchen is embedded within the House logic and there is nothing we can do in a test to prevent the Kitchen from getting instantiated. We say that we are mixing the concern of application instantiation with concern of application logic. In order to achieve true unit testing we need to instantiate real House with a fake Kitchen so that we can unit-test the House in isolation.
class House {
private final Kitchen kitchen;
private boolean isLocked;
public House(Kitchen kitchen) {
this.kitchen = kitchen;
}
private boolean isLocked() {
return isLocked;
}
private boolean lock() {
kitchen.lock();
isLocked = true;
}
}
Notice how we have removed the new operator from the application logic. This makes testing easy. To test we simply new-up a real House and use a mocking framework to create a fake Kitchen. This way we can still test House in isolation even if it is not a leaf of an application graph.
But where have the new operators gone? Well, we need a factory object which is responsible for instantiating the whole object graph of the application. An example of what such an object may look like is below. Notice how all of the new operators from your applications migrate here.
class ApplicationBuilder {
House build() {
return new House(new Kitchen(
new Sink(),
new Dishwasher(),
new Refrigerator())
);
}
}
As a result your main method simply asks the ApplicationBuilder to construct the object graph for you application and then fires of the application by calling a method which does work.
class Main {
public static void main(String...args) {
House house = new ApplicationBuilder().build();
house.lock();
}
}
Asking for your dependencies instead of constructing them withing the application logic is called “Dependency Injection” and is nothing new in the unit-testing world. But the reason why Dependency Injection is so important is that within unit-tests you want to test a small subset of your application. The requirement is that you can construct that small subset of the application independently of the whole system. If you mix application logic with graph construction (the new operator) unit-testing becomes impossible for anything but the leaf nodes in your application. Without Dependency Injection the only kind of testing you can do is scenario-testing, where you instantiate the whole application and than pretend to be the user in some automated way.



13 responses so far ↓
How to Write 3v1L, Untestable Code | Miško Hevery // Jul 25, 2008 at 1:28 pm
[...] Cover your ears when some testing weenie tells you about Dependency Injection, Law of Demeter, or not looking for [...]
Nice article. A few surprisingly good concepts:
- Summary in the beginning
- using a “House” and a “Kitchen” instead of “foo” and “bar”
- looking at an application as a graph with nodes an leafs.
Where Have All the Singletons Gone? | Miško Hevery // Aug 21, 2008 at 11:50 am
[...] the new operators are mixed with application logic (see: How to Think About the new Operator) then the Constructor Graph and the Collaborator Graph tend to be one and the same. However, in an [...]
Jon Lebensold » Blog Archive » What is Dependency Injection? Go Ask a Java Nerd! // Aug 30, 2008 at 3:32 am
[...] fell upon an excellent post by Misko Hevery where he describes how we should treat the “new” operator. He inadvertently explained Dependency Injection in a really clear and simple way using a House and [...]
Where Have all the “new” Operators Gone | Miško Hevery // Sep 10, 2008 at 10:25 am
[...] of running the application. The reason that this separation is important was outlined in How to Think About the “new” Operator. So let us look at where have all of the new operators [...]
Application Wiring on Auto-Pilot | Miško Hevery // Sep 24, 2008 at 10:35 am
[...] talked about how it is important to separate the new operators from the application logic. This separation forces your code to have factories which are responsible for wiring your [...]
Singleton I love you, but you're bringing me down | // Coding Without Comments // Oct 9, 2008 at 4:27 am
[...] google) goes into much greater detail about this technique in his two fantastic blog posts entitled “How to Think About the ‘new’ Operator with Respect to Unit Testing” and “Where Have All the Singletons [...]
Dependency Injection Myth: Reference Passing | Miško Hevery // Oct 22, 2008 at 7:57 am
[...] that mean that dependency injection is wrong? NO! it means that you only did half of the work! In how to think about the new operator we go into the details why it is important to separate your business logic from the new operators. [...]
dholm.com » Blog Archive » Tumblelog: 081117 // Nov 17, 2008 at 12:07 am
[...] How to Think About the “new” Operator with Respect to Unit Testing is an excellent introduction to dependency injection and why you need it. Also read How to Write 3v1L, Untestable Code. [...]
First of all, I really like your articles and the videos as well. I am learning a lot.
Now, just to be silly. I think you have not unit-tested your main method very well in the above example:
public static void(String…args) {
should probably be:
public static void main(String…args) {
Keep up the good work!
Static Methods are Death to Testability // Dec 15, 2008 at 10:50 am
[...] method, one would have to instantiate the object first, and that may prove to be a problem. (See how to think about the new operator, and class does real [...]
hi there! i m an as3 developper and your articles are very interesting! I am definetly ok with using factories instead of using new operator in myh class… but what if the obejcts created hav a big “tree architecture”? i mean what if the house has a kitchen whitch has a sink, which has a tap which is made of many other things… don’ t you feel like the factory method for creating this object will get you nuts?? (sorry for my poor english)
Interfacing with hard-to-test third-party code // Jan 4, 2009 at 12:28 pm
[...] Servlets require a no argument constructor which prevents us from using dependency injection. See how to think about the new operator. [...]
Leave a Comment