views:

162

answers:

6

Hi,

I have a set of classes that have dependencies at instantiation time, that is, when creating an object of type A, it also creates another of type B, which subsequently creates others of type C, etc.

For testing matters, I don't need the whole functionality of all the levels to test the upper ones, so I could use stubs or mocks, but as I have explicit new's in the constructors I cannot see a direct way other than changing the code to use an AbstractFactory and provide one that creates the fakes at testing time.

So, is there any "black magic" way to hack the Java class loader so the fake testing classes are created instead the normal ones when instantiating objects with new?

Thanks!

+7  A: 

Why not add a constructor which takes those dependencies as parameters, instead of creating them yourself in the constructor? Personally I'd add that one and remove the other one :) Injecting the dependencies makes the code easier to test and more flexible in the future (as you can easily inject a different implementation later on, without changing the code.)

Jon Skeet
That's not what I was asking (because I already knew that, as it can be read in the question), what I want to know is if I can bypass somehow the class loader :-/
fortran
@fortran: Where in the question does it acknowledge that your constructor is basically doing too much? If there *are* ways of doing this, they will be very ugly indeed. Why aren't you injecting your depencies in the simple, normal way?
Jon Skeet
because I'm too lazy :-p
fortran
and there goes the reason for most software being crappy ;)
Bozho
Don't think so, laziness is what makes people to write less code, and hence introduce less bugs! XD
fortran
@fortran: Which do you *really* think is going to be simpler and take less time: hijacking a classloader, or introducing a constructor parameter or two?
Jon Skeet
@Jon Skeet Even if it's more complicated and takes more time, I *really* do prefer complex tests for simple code instead of simple tests for convoluted code.
fortran
@fortran: In what way is passing in a dependency "convoluted"? Quite the reverse: it's better design IMO than instantiating your dependencies yourself.
Jon Skeet
@Jon Skeet compare `new Foo()` vs adding a parameter to the method signature (the constructor in this case) and doing `factory.createFoo()`, plus declaring the interface for the factory and implementing it.
fortran
@fortran: Well, it's your call. I still recommend that you change to a simpler, more testable design. You don't need to use a factory unless you want one, of course... and you might consider using one of the *many* dependency injection frameworks available.
Jon Skeet
@Jon Skeet Come on, that is clearly overkill and goes against keeping it simple and stupid.
fortran
@fortran: No, it's *not* clearly overkill. You have dependencies, so inject them. The "no frills" way is to just construct them directly in the caller; using a DI framework is appropriate for larger applications. What *would* be overkill IMO would be exactly what you're proposing: working against the language in order to inject dependencies *slightly later* than is really convenient.
Jon Skeet
@Jon Skeet I still think for this case using dependency injection is an artificially created necessity, as there are no other situation (besides testing) in any foreseeable future where the implementation classes would need to be replaced in the project I'm working. And maybe I'm wrong, but I like to prioritize the clarity and simplicity of the real code that is going to be deployed above those of the testing code.
fortran
@fortran: Passing in the dependencies adds clarity to the design, IMO - it shows what you're depending on, makes it easier to practise TDD, and is still simple. You don't need to use a fancy framework of course (in the last few projects all my dependencies have been hand-injected, but the components themselves have been nicely testable) but making the dependencies clear really helps readability, IMO. Feel free to disagree, but don't be surprised when your code is hard to test *and* hard to quickly work out the dependencies.
Jon Skeet
@Jon Skeet: Come on, Jon. Just look at the JMockit example test and than tell me it is not as simple as it can possibly be. The test also proves that code which instantiates dependencies directly is perfectly and easily testable. DI is not really meant to be always used; in fact, it is only supposed to be used for *externally configurable* dependencies, not for *internal, fixed* ones.
Rogerio
@Rogerio: To me, it's still fighting the language/platform. When you say DI is "not supposed to be used for internal, fixed dependencies" (*very* slight paragraph) - supposed by who? It still feels like a good idea to me to make dependencies clear and inject them where possible.
Jon Skeet
@Jon: JMockit only uses standard Java packages, with no external dependencies; how can that mean "fighting the platform"? Something like OSGi, for example, is much more "deviant", in comparison. Concerning DI, I was talking about Martin Fowler's original article, of course. According to it, DI is simply *one* technique to "separate configuration from use"; unfortunately, for whatever reasons many seem to ignore the original advice, prefering to abuse the technique (very much like what happened with Design Patterns, I would say). And from what I see in real projects, DI *is* seriously abused.
Rogerio
@Rogerio: When a constructor call no longer *actually* just calls the specified constructor, that feels like fighting the platform to me. As for Fowler's original article: I like to think that techniques don't have to be stagnant - they can adapt as time goes on. It sounds like we have very different ideas of the usefulness of DI though, so I'm not sure we're going to get much further on this.
Jon Skeet
@Jon: Sure, but intercepting the execution of a constructor is the same as intercepting the execution of a method, isn't it? All mocking tools do that in one way or another, and some other tools (AOP, profilers, Java agents in general) also have the ability to intercept constructors. It's nothing really new. You seem to imply that DI as described in Fowler's article is no longer the "official" version. I can't find evidence of that. Any pointers?
Rogerio
@Rogerio: I'm implying there *is* no "official" version. It's not like everyone has to comply to one strict set of rules - figure out where it makes sense for you. As for intercepting execution: I typically mock interfaces rather than classes to start with, so really the mocking framework is just making it easier to *provide* an implementation rather than *intercepting* one. As interfaces are inherently virtual, when you write code calling an interface you *expect* it to be "some unspecified implementation". Compare that with a constructor call, where you normally know exactly what will happen
Jon Skeet
@Jon: Ok, I agree on having multiple interpretations or levels of use for DI. In the end, I have no problem with it, provided the resulting code does not become bloated. Mocking interfaces is perfectly fine, of course; *creating* a separate interface *just* to have it mocked is what I (and most developers, including you, I hope) don't like (and this is probably the reason why mocking of classes is a popular demand).
Rogerio
@Rogerio: Well, I'm suspect we disagree there. I have definitely created interfaces solely for the purpose of DI - but not just for the purpose of mocking. I find that having the interface makes it easier to see what I'm depending on; I can "zoom in" on one particular class if I can see its dependencies just in terms of interface rather than implementation. The fact that it makes it easier to test goes hand-in-hand with that, of course.
Jon Skeet
@Jon: Yes, I see what you're talking about; the argument that DI lets you easily see the dependencies is a common one. I never bought it though, as to me looking at the list of import statements is enough (or perhaps using a nice graphical tool, such as the UML support in IntelliJ IDEA). Also, I prefer to keep *internal* dependencies of a class hidden from its clients; for example, when using the Apache Commons EMail API inside a business service class, I see no point in exposing this fact to client classes, which don't really need to know that an e-mail message gets sent.
Rogerio
@Rogerio: The import statements show you *which classes* are used, but not what their API is, or what part of it you're interested in. Often the public API of a class ends up being bigger than the inferface you might extract, for example. As for the internal/external dependency part - that's where Guice's modules would probably be relevant to you, but it really depends on what you're doing. Let's just say that I've never found it to be a problem - but neither of us has worked in every imaginable situation :)
Jon Skeet
+2  A: 

What do you want is to use mock classes. Consider using any framework for that. Here is a good one - https://jmockit.dev.java.net/

Jayan
Or JMock or one of the many mocking libraries. You can even use Proxy.newProxy() directly, which most of these use.
Peter Lawrey
+2  A: 

You can achieve the intended behaviour without modifying the original sources by modifying the classpath. Create dummy classes in a second source folder with the exact same names and packages. Then put the dummy classes to the classpath and remove the real ones.

This works best with classes that are on their own jar, such that you can just exchange the jars.

Arne
A: 

After some research, it seems that PowerMock can do it very well:

http://code.google.com/p/powermock/wiki/MockConstructor

fortran
A: 

JMockit fits the requirement perfectly. For example, you could write a test like this:

// In production code:
public final class ClassUnderTest
{
    private final SomeDependency dep;

    public ClassUnderTest(String someData)
    {
        dep = new SomeDependency(someData);
    }

    void methodToBeTested() { ... int i = dep.doSomething("xpto", true); ... }
}

final class SomeDependency
{
    int doSomething(String s, boolean b) { ... }
}

// In test code:
public final class MyTest
{
    @Test
    public void mockingInternalDependencies()
    {
        new Expectations()
        {
            SomeDependency mockedDep;

            {
                mockedDep.doSomething(anyString, anyBoolean); result = 123;
            }
        };

        new ClassUnderTest("blah blah").methodToBeTested();
    }
}
Rogerio
A: 

Another alternative, as I see you don't want to create a constructor, nor use DI, could be have the attribute you need to mock with default visibility and create factories for test (with the same package and in other source folder) that inject directly the mock objects.

Pau