How Dependency Injection Fosters Testability
Asked Answered
M

6

7

I've been reading up on the Factory pattern, and have come across articles that suggest using the Factory pattern in conjunction with dependency injection to maximize reusability and testability. Although I have not been able to find any concrete examples of this Factory-DI hybrid, I'm going to try and give some code examples of my interpretation. However, my question is really about how this approach improves testability.

My interpretation:

So we have a Widget class:

public class Widget {
    // blah
}

And we want to include a WidgetFactory to control the construction of Widgets:

public interface WidgetFactory {

    public abstract static Widget getWidget();
}

public class StandardWidgetFactory implements WidgetFactory {

    @Override
    public final static Widget getWidget() {
        // Creates normal Widgets
    }
}

public class TestWidgetFactory implements WidgetFactory {

    @Override
    public final static Widget getWidget() {
        // Creates test/mock Widgets for unit testing purposes
    }
}

Although this example uses Spring DI (that's the only API I have experience with), it doesn't really matter if we're talking about Guice or any other IoC framework; the idea here is that we're now going to inject the correct WidgetFactory implementation at runtime to depending on whether we are testing the code or running normally. In Spring the beans config might look like this:

<bean id="widget-factory" class="org.me.myproject.StandardWidgetFactory"/>
<bean id="test-widget-factory" class="org.me.myproject.TestWidgetFactory"/>

<bean id="injected-factory" ref="${valueWillBeStdOrTestDependingOnEnvProp}"/>

Then, in the code:

WidgetFactory wf = applicationContext.getBean("injected-factory");
Widget w = wf.getWidget();

This way, an environmental (deployment-level) variable, perhaps defined in a .properties file somewhere, decides whether Spring DI will inject a StandardWidgetFactory or a TestWidgetFactory.

Am I doing it right?!? This seems like an awful lot of infrastructure just obtain good testability for my Widget. Not that I'm opposed to it, but it just feels like over-engineering to me.

My hangup:

The reason why I am asking this is because I will have other objects in other packages, which have methods that use Widget objects inside of them. Perhaps something like:

public class Fizz {
    public void doSomething() {

        WidgetFactory wf = applicationContext.getBean("injected-factory");
        Widget widget = wf.getWidget();

        int foo = widget.calculatePremable(this.rippleFactor);

        doSomethingElse(foo);
    }
}

Without this huge, seemingly-over-engineered setup, there would be no way for me to inject "mock Widgets" into my unit test for Fizz::doSomething().

So I'm torn: on one end it just feels like I'm overthinking things - which I may very well be doing (if my interpretation is incorrect). On the other hand I don't see any clean way to get around it.

As a segue into a tangential question, this also raises another huge concern of mine: if my interpretation is correct (or even somewhat correct), then does this mean we need Factories for every object?!?

That sounds like way-overengineering! What's the cut-off? What's the line in the sand that demarcates when to use a Factory, and when not to?! Thanks for any help and apologies for a verbose question. It's just got my head spinning.

Minier answered 20/12, 2011 at 15:50 Comment(1)
Note: You cannot declare abstract static methods or override static methods.Jamboree
L
6

DI/IoC helps testing because you can decide, easily, what implementation to use, without modifying the code that uses it. This means you can inject a known implementation to exercise specific functionality, e.g., simulate a web service failure, guarantee good (or bad) input to a function, etc.

Factories are not required to make DI/IoC work. Whether or not a factory is required depends entirely on usage specifics.

public class Fizz {

    @Inject    // Guice, new JEE, etc. or
    @Autowired // Spring, or
    private Widget widget;

    public void doSomething() {
        int foo = widget.calculatePremable(this.rippleFactor);
        doSomethingElse(foo);
    }

}
Landri answered 20/12, 2011 at 15:55 Comment(3)
Thanks! Any chance you could modify your answer to indicate if I've set up DI correctly, and/or show me an example of how to write Fizz::doSomething() without the need for a WidgetFactory?Minier
And what if I need different Widgets inside different Fizz methods? I assume in that case that an injected WidgetFactory is appropriate?Minier
@AdamTannon Possibly, although without knowing the actual usecase it's hard to say.Landri
Q
2

In your Fizz example class, it's a DI anti-pattern to make an explicit call into the DI framework to get the bean you want. The whole point of DI is the Hollywood principle (don't call us, we'll call you). So your DI framework should be injecting a Widget into Fizz.

When it comes to testing, my preference is

  • the test fixture creates a stub or mock of the dependency. Or the real thing when it's appropriate.
  • the test fixture injects the stub using the same constructor or setter method that, in production, would be used by my DI framework.

Some people like to run tests inside their DI container, but I don't see the point in this for unit tests - it just creates an extra lot of configuration to maintain. (For integration tests it's worthwhile, but you should still have as much of your DI context as possible in common between the production and integration-test contexts.)

The conclusion of all that is that I don't see a whole lot of use to the Factory pattern.

Can you give us a link to the article(s) you've been reading? I'll see if I can dig up one or two myself.

Edit: Here's the promised link: Is Dependency Injection Replacing the Factory Patterns?, one in a series of rather good articles about DI.

Quetzal answered 20/12, 2011 at 15:58 Comment(10)
Thanks Andrew - but doesn't that mean that I would need to make a Widget member inside Fizz (so that I can set it as a property)? If thats the case, then I would need to create instance variables for every single class used throughout every Fizz method...something doesn't feel right there. I could end up with Fizz having 30 instance variables!Minier
DI is not a replacement for factories; they solve different problems--in the section labeled "how does DI solve this" in your linked article there's still an object factory (and is explicitly called as much). DI can solve some uses of factories.Landri
@AdamTannon - If you keep your classes small and dedicated to specific purposes you will not have the problem of an excessive number of instance variables.Eke
@AdamTannon The whole point of DI is to let a container do the lifting for you. But yes, ideally, in a loosely-coupled system, instance variables would be injected, relieving the class of instantiation responsibilities.Landri
@AdamTannon If you want to inject it in the usual DI manner, yes. Though if Fizz is using 30 other classes, it probably has too many responsibilities and needs breaking up.Quetzal
It all depends on what kind of object Widget is. Is it a stateless helper object? Then you can (IMO) instantiate it at will, though the DI purists will disagree. Is it a longlived or shortlived object? You might want to inject the WidgetFactory into Fizz instead, and then use it to get your Widgets when you need them. In all cases, it's good practice if your classes don't explicitly know about the DI framework that's manipulating them. Keeps things flexible; e.g. you can build them by hand in your unit tests if need be.Quetzal
@AndrewSpencer It's not statelessness that's a factor, it's functionality: stateless classes can still implement behavior that needs to be switched out for testing, during runtime, etc.Landri
@Dave Newton that's true. But if there's no behaviour you want to switch out, you still might find yourself using DI, if you wanted to inject some configuration from the DI context. (I never thought about it before, but DI is used to inject both behaviour and configuration, which are quite separate things...)Quetzal
@AndrewSpencer I didn't say you wouldn't use DI, I was taking issue with the idea that state (of which configuration is one) is the gating factor. They're not that separate, though; configuration is either state, or behavioral definitions.Landri
let us continue this discussion in chatQuetzal
T
2

I think the answer to this question, at its core, is simple : The benefits of Dependency Injection can be realized using normal java, but at the cost of adding lots of boiler plate.

DI allows you to decouple and modularize your classes without necessarily adding a bunch of parameterized constructors / Factory methods, etc to your source code base.

Tolu answered 20/12, 2011 at 16:10 Comment(0)
E
0

You have a factory, but you are not using Dependency Injection in your Fizz class. That is why you are not having an easy time testing it. You need to inject either the applicationContext or the WidgetFactory directly. I prefer the latter in most cases.

public class Fizz {
    private WidgetFactory wf;
    public Fizz(WidgetFactory widgetFactory) {
        wf = widgetFactory;
    }

    public void doSomething() {

        Widget widget = wf.getWidget();

        int foo = widget.calculatePremable(this.rippleFactor);

        doSomethingElse(foo);
    }
}

Now that you have this, you can inject mock objects into your Fizz class. In the example below I use Mockito, but you can use other mocking frameworks (or make your own mock, if you really want).

@Test
public void test() {
    WidgetFactory wf = mock(WidgetFactory.class); // mock is a Mockito function that creates mocks for you automatically.
    Fizz objectUnderTest = new Fizz(wf);

    // Test Fizz
}

In your unit testing you really don't want to load an applicationContext. That is why you mock it in code and then pass it in directly. Thus you will have only one simple applicationContext and that for production.

<bean id="widget-factory" class="org.me.myproject.StandardWidgetFactory"/>
<bean id="fizz" class="org.me.myproject.Fizz">
    <constructor-arg ref="widget-factory"/>
</bean>

In regard to you question about needing a factory for everything, no you don't need a factory for everything. For objects which my class uses and does not create, I prefer to use DI and pass it in through a constructor. You can also pass it in through a setter if it is optional. If the class does create a new object then a factory may be appropriate. As you begin to learn how to use DI, factories, and write testable code I think you will be able to get a feel for when it is too much to write a factory or not.

Eke answered 20/12, 2011 at 16:3 Comment(3)
David - thanks for your very thorough answer and helpful code samples! You've helped me take the next step in comprehending this stuff. I do have the same question for you as I had for @Andrew Spencer: This arrangement forces me to add an instance variable to Fizz's design for every object I want to inject into it. So, this means that if Fizz has 10 methods, and inside each of those 10 methods are a total of, say, 15 different objects (defined elsewhere in other packages), then I need to have 15 instance variables for each of them! Is this correct or am I missing something?!?Minier
Yes, and if you have 15 dependencies in your class than that's something you want to think about - does it really need all these things to do it's job? Whenever I see a class with more than a few injected services I try and figure out a way to refactorUlysses
@AdamTannon - I mentioned in the comments in Andres Spencer's post that you will not have this problem if you make your classes small and give them more specific purposes. Small classes with limited purposes are more reusable and easier to test anyway, so I encourage it in general. You could refactor Fizz, or you could try to apply DI (with or without a factory) on a smaller class first. That will help you to better understand the concepts.Eke
O
0

Dependency injection definitely makes code more testable. When I come to untested EJB3 code that used the Dependency injection, it is like night and day compared to getting other types of legacy code under test.

As for factories, they can have two roles. One is dependency injection when you don't have a dependency injection framework. This is a static factory pattern, spoken about in effective Java, which is actually rather unique to Java, and best done for other reasons (elaborated there). You see some of that in the JDK, and it is a bear for testing, and certainly doesn't make testable code naturally, as it is something you have to be very conscious about using.

Another role for factories, where you are using the Gang of Four true pattern, in dependency injection is where the construction of the actual object is somewhat involved, not a simple constructor. In that case, you inject a factory, which can be represented in a simple interface, and then have the factory implementation do the heavy lifting. This is more built around the principle of in programming, there is no problem that cannot be solved with yet another layer of indirection.

It actually wasn't until I started doing Guice dependency injection that I found a need for the GOF factory pattern in Java.

Overdevelop answered 20/12, 2011 at 16:9 Comment(0)
F
0

DI enables you to program to interfaces without having to use factories. Programming to interfaces and the ability to pass in a concrete implementation form outside of the class is what makes code easier to test.

interface Widget {
}

class UserOfWidget {
   Widget widget;
   public UserOfWidget(Widget widget) {
      this.widget = widget
   }
}

Using spring dependency injection
<bean id="widget" class="ConcreteWidget"/>
<bean class="UserOfWidget">
   <constructor-arg ref="widget"/>
</bean>

In a test case
UserOfWidget uow = new UserOfWidget(new StubWidget()); 

Without DI to achieve the same you would need to hide the creation of the concrete Widget behind a factory - similar to the messy code that you have in your example.

That said there is a case for using Factories even when you are using DI. Mostly for scenarios where the construction of the object is complex - requires multiple steps, cannot be done by just a simple constructor call. One such example is retrieving an object from JNDI (a datasource for example). All the logic to lookup the object from JNDI is encapsulated in an factory - which then can be configured as a simple bean in the XML file.

To summarize - you don't need factories with DI to make code more testable. Just DI is enough. Factories are only required to simplify creation of objects that are complex to create.

Fulcher answered 20/12, 2011 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.