There's a lot of tools that exist to make what developers to easier to do. If it's something we do a lot, we tend to find ways to have something do it for us.
I've done it. I'll probably do it again. I built a little tool to automate the generation of Android Services based off JSON because I spent so much time copy/pasting existing services and editing them into the new serevice. Copy and Paste means you're missing an abstraction - I couldn't find it, so I built a tool to deal with it.
We find challenges and difficulties accomplishing what we want to do, so we find things we can do to reduce that pain, to simplify the process. It's one of the things I really enjoy about being a developer - I can simplify things I want to do.
The downside of the things that get built, is they get built for the way that developers write code and that's very often not inline with the technical practices. These tools promote writing code in the fashion they're built to simplify. I find that they make it very hard to write good object oriented code. It's simpler to write (not maintain) bad OO code, especially with these tools.
I was a pro with PowerMock in my Android days. I get really annoyed when I can't do what I want, and I'm lucky that people have built the tools I can use to beat the system into submission.
The biggest uses I recall using from powermock were:
- Frack your Final!
- Static means NOTHING!
- Instantiate what now?
I have a lot of respect for the talent and understanding the developers of PowerMock. This quote from their readme is fantastic
Please note that PowerMock is mainly intended for people with expert knowledge in unit testing. Putting it in the hands of junior developers may cause more harm than good.
I think powermock is still a hugely useful tool - In the case of supporting legacy code. It enables getting tests around functionality with the smallest amount of refactoring... It has a place, BUT... It should always be the engineers goal to remove the need for PowerMock.
Let's look at the ways my biggest uses impacted design
Frack your final!
PowerMock has the great ability to remove the
final aspect of a class so that it can be Mocked (another post on how mocking sucks it up is in the works).
When we could then mock a class... we'd have pass it into something else. Why'd we have to remove the
final? Because the class/consumer didn't implement/accept an interface. A properly encapsulated class has REQUIRED it's encapsulation be broken because it did not implment a contract. We should never need to de-
final a class. Everything should be implementing a contract and collaborating against a contract.
I used this sometimes to be able to derive from the class so I could override behavior and/or access protected fields/methods to interrogate or test the class.
These are the wrong ways to do it. If you need to test behavior and it's locked behind a
private access - Then you're missing an object to represent and make testable that behavior.
The ability PowerMock provides to break the
final encapsulation of a class allows us to do all the things
final is supposed to help us in the design of our system.
- Avoid implementing contracts
- Interogate beyond encapsulation
- Leave objects uncreated
The ability to bypass what the code is telling us it needs prevents the design emerging into a better form.
Static means NOTHING!
Utils classes. The swatch of static methods that provide functionality I've seen... and written... They get used everywhere and that's behavior that our class becomes tightly coupled to.
These things tend to make it REALLY hard to test, especially when those
Utils wrap system interactions... They really want to keep interacting with the file system... or other 3rd party systems.
PowerMock enables replacement of the static methods for your tests. Which is AWESOME... but it doesn't force the system to change, to be better.
Static methods are untestable and reprsent a missing object in an OBJECT ORIENTED system. PowerMock's ability to replace the method allows us to ignore the objects we're missing.
We don't want to ignore objects crying to be created. We're in an object oriented langauge and we should create the objects that are begging to exist.
I'm sure I've got a technical practices post about why we don't want static methods, but for now - It's because we should have objects.
Instantiate what now?
No New Inline. One of the key technical practices that drive better design into the code.
New-ing up a collaborator causes tight coupling.
Tight Coupling isn't JUST about being hard to test. That's a significant impact and detriment to the system, absolutely.
It's also about design. It's about using contracts.
I'm working in a system that interfaces almost everything - When I look at the interfaces, ReSharper tells me that the interface methods are all unused. This is because the implementation of those interfaces are all new-ed up inline. The contract is not enforced because all calls are based on the concrete type.
This is a huge sign that we're not instantiating at the correct places.
With PowerMock, we can replace all instantiations of a given class with something defined in our tests. We just up and replace what's instantiated with whatever we want... Which... neat... But NOOOOO!
The ability to bypass the technical practices which drive the code towards a better state simply because the test coverage increases is a detriment. This tightly couples the tests to the implementation.
When tests are tightly coupled to the implementation of the class, that class resists refactoring, making it a class that people stop wanting to touch. Despite a great view on what legacy code is:
Legacy code is code w/o tests
I prefer to look at it as
Legacy code is code that no one can safely change.
If you can't change it safely, test coverage or otherwise; then it's legacy code.
I think all other of these "Tool Impact on Developer Discpline" posts are technologies I have abandoned and hope to drive out of code I'm active in. PowerMock is an exception; for one and only one case I can think of - Legacy Code.
If you're working on legacy code, you can get behavior tests in place with PowerMock and then understand the system well enough to refactor it. You'll break the PowerMock tests, but be able to replace what it tested with a new test. Or what it was testing is no longer valid.
PowerMock can be critical in getting an untested legacy system under control. Once it's under control PowerMock should go away.