views:

554

answers:

6

In my profiler reports I'm increasingly seeing the results of mock-based testing with dependency injection. Many of the dependencies were static, but because we want to test methods in isolation they are changed to instance members, like the following example:

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   // Constructor used in production 
   public ShortLivedThing() {
     dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3();
   }

   // DI for testing 
   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1(); dep2 = d2(); dep3 = d3();
   }
}

In turn the dependencies most of the time have other dependencies and so on. This results in the instantiation of a tree of (mostly "static") objects every time a method call is done outside of tests. Each of the objects are very small (just a few pointers), but the tree effect turns this into an ever increasing performance hit.

What can we do about it?

+7  A: 

It seems to me like you need to leverage the features a proper dependency injection framework can give you. Do not use different construction logic for testing/production.

With spring, singleton injections are only performed at container startup. Prototype injections are done every time. The full wiring is also done each time you run a unit test, if it's being wired. So profiling unit tests is generally not a good idea.

Maybe you're using too little of the singleton scopes and too much prototype scope ? (Prototype = new instance every time)

The nice thing about spring injection is that you can use scope proxies, meaning your object graph can look like this:

 A Singleton
 |
 B Singleton
 |
 C Prototype (per-invocation)
 |
 D Singleton
 |
 E Session scope (web app)
 |
 F Singleton

And each request would only create 1 instance of C and one instance of E per session. A, B, D and F are singletons. If it's not a webapp you dont have session scope by default, but you can also make custom scopes (a "Window" scope sounds cool for a windowed desktop app). The clue here is that you can "introduce" scopes at any level, effectively you can have ten layers of singleton objects and all of a sudden something session scoped shows up. (This can really revolutionize how you implement some cross-cutting features in a layered architecture but that's a different story)

This really gives the minimum object creation possible within a DI model, I think.

Although this is Spring for Java I believe a number of other DI frameworks should support similar features. Maybe not the most minimalistic ones.

krosenvold
Is F given the E object? If so, how does this work with multiple users?
Lasse V. Karlsen
I was thinking top to bottom, and D is given the E. Dynamic proxies or cglib does the magic.
krosenvold
And yes, it works really well with multiple users. 300 characters gets slighlty small. Check out http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes
krosenvold
+1  A: 

Hi Bjorn,

how about passing in references?

Regards Torsten

A: 

The best I can come up with is to put all the dependencies into one "context" object, which is then shared among all the instances. This should mitigate the performance problem somewhat.

starblue
+1  A: 

If your concern is the slow tests, try running them in parallel and do not let the testing process interrupt your programmers.

Automate this process:

  • When some one checks in, make a build out of the repository.
  • Run tests on this build.
  • E - Mail the results to the developer who checked in.

It is better if the first check in is not done to the actual repository. Make it to a temporary one and make the build out of this. Optionally you can perform performance tests, style checks etc. and include these in the e - mail. If you do this, add one step to automated process:

  • If tests pass (and optional criteria is met), merge the new code with the actual repository.

By this way, slow tests are no concern. Also when a developer needs to know if her code broke something or made the performance boost she expected, she just checks in and waits for the e - mail generated for her.

Serhat Özgel
+2  A: 

I think you should only have the "DI constructor". You call this constructor for testing as well as in production.

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1; dep2 = d2; dep3 = d3;
   }
}

This way you don't have the problem of instantiating a tree of objects every time a method call is done outside of your tests. Of course, for production you have to wire up your objects correctly outside the participating objects themselves, which is a good thing.

In summary: don't go for 50% DI / 50% hardcoding, go for 100% DI.

eljenso
A: 

If you are targeting .NET, check out the Autofac. It has various scopes (singleton, factory, container) to tweak the creation aspects, deterministic disposal to keep resource usage at bay and allows to use GeneratedFactories and lambda expressions to configure components and avoid the cost of reflection.

Rinat Abdullin