views:

2374

answers:

5

I've been a fan of EasyMock for many years now, and thanks to SO I came across references to PowerMock and it's ability to mock Constructors and static methods, both of which cause problems when retrofitting tests to a legacy codebase.

Obviously one of the huge benefits of unit testing (and TDD) is the way it leads to (forces?) a much cleaner design, and it seems to me that the introduction of PowerMock may detract from that. I would see this mostly manifesting itself as:

  1. Going back to initialising collaborators rather than injecting them
  2. Using statics rather than making the method be owned by a collaborator

In addition to this, something doesn't quite sit right with me about my code being bytecode manipulated for the test. I can't really give a concrete reason for this, just that it makes me feel a little uneasy as it's just for the test and not for production.

At my current gig we're really pushing for the unit tests as a way for people to improve their coding practices and it feels like introducing PowerMock into the equation may let people skip that step somewhat and so I'm loathe to start using it. Having said that, I can really see where making use of it can cut down on the amount of refactoring that needs to be done to start testing a class.

I guess my question is, what are peoples experiences of using PowerMock (or any other similar library) for these features, would you make use of them and how much overall do you want your tests influencing your design?

+1  A: 

I think you're right to be concerned. Refactoring legacy code to be testable isn't that hard in most cases once you've learned how.

Better to go a bit slower and have a supportive environment for learning than take a short cut and learn bad habits.

(And I just read this and feel like it is relevant.)

Jeffrey Fredrick
+1  A: 

I think you're right - if you need PowerMock, you probably have smelly code. Get rid of those statics.

However, I think you're wrong about bytecode instrumentation. I mock out concrete classes all the time using mockito - it keeps me from having to write an interface for every. single. class. That is much cleaner.

Only you can prevent code smells.

SamBeran
I do use the EasyMock class extension to mock concrete classes, but I only do that for existing classes that would be too much work to refactor, but the fact that they're mocks BEFORE going into my class seems ok to me
MrWiggles
+2  A: 

We've had many of the same questions arise in the .NET arena, regarding Typemock Isolator. See This blog post

I think that when people start to realize that Testability is not an end goal, and that design is learned in other ways, then we will stop letting our fear dictate which tools we use, or not use a more advanced technology when and if it becomes relevant.

Also, it makes sense to be able to choose the way you design, based on the application needs. don't let a tool tell you how to design - it will leave you no choice.

(I work at Typemock, but was once against it)

RoyOsherove
+2  A: 

I totally agree that Testability is not an end goal, this has been one of the things I have realized when developing PowerMock. I also agree that writing unit tests is one way of getting good design. Using PowerMock should probably be an exception rather than a rule, at least features such as expectations on constructors and static mocking.

The main motivation we have for using PowerMock is when using third party code that prevents your code from being testable. A good alternative is using an anti-corruption-layer that abstracts the third party code and makes it testable. However, sometimes I think the code is cleaner just using the standard APIs. A good example of this is the Java ME API. This is full of static method calls that prevent unit testing.

The same problem can occur with legacy code. Some organizations are extremely afraid of modifying their existing code and in this case PowerMock can be used to introduce unit testing in the parts you are writing at the moment, without forcing big refactorings.

Our problem is specifying a set of best practice rules when to use PowerMock or not that a rookie developer can follow. Creating good design is really hard and since PowerMock gives you more options, maybe it just gets harder for a beginner? I think a more experienced developer appreciates having more choices.

(founder of PowerMock)

Jan Kronquist
Hi Jan, thanks for your response and I agree with what you say totally and that's where my concern comes from. The experienced guys will know to use it sparingly, it's the novices that worry me and that they'd just start using it where refactoring the code would be a better idea.
MrWiggles
+5  A: 

I have to strongly disagree with this question.

There is no justification for a mocking tool that limits design choices. It's not just static methods that are ruled out by EasyMock, EasyMock Class Extension, jMock, Mockito, and others. These tools also prevent you from declaring classes and methods final, and that alone is a very bad thing. (If you need one authoritative source that defends the use of final for classes and methods, see the "Effective Java" book, or watch this presentation from the author.)

And "initialising collaborators rather than injecting them" often is the best design, in my experience. If you decompose a class that solves some complex problem by creating helper classes that are instantiated from that class, you can take advantage of the ability to safely pass specific data to those child objects, while at the same time hiding them from client code (which provided the full data used in the high-level operation). Exposing such helper classes in the public API violates the principle of information hiding, breaking encapsulation and increasing the complexity of client code.

The abuse of DI leads to stateless objects which really should be stateful because they will almost always operate on data that is specific to the business operation. This is not only true for non-public helper classes, but also for public "business service" classes called from UI/presentation objects. Such service classes are usually internal code (to a single business application) that is inherently not reusable and have only a few clients (often only one) because such code is by nature domain/use-case specific. In such a case (a very common one, by the way) it makes much more sense to have the UI class directly instantiate the business service class, passing data provided by the user through a constructor.

Being able to easily write unit tests for code like this is precisely what led me to create the JMockit toolkit. I wasn't thinking about legacy code, but about simplicity and economy of design. The results I achieved so far convinced me that testability really is a function of two variables: the maintainability of production code, and the limitations of the mocking tool used to test that code. So, if you remove all limitations from the mocking tool, what do you get?

Rogerio