Hacker News new | ask | show | jobs
by _asummers 4202 days ago
Additionally, if you need to mock that doSomething() method's behavior at all, you can't just subclass and override the method. You would need to bring in byte code voodoo (using e.g. PowerMock in Java).
2 comments

And PowerMock is the bane of all existence!

It can be a useful tool, but it causes all kinds of weirdness which can completely mitigate any advantages. On my current project I had to strip out most of our uses of PowerMock. While the tests pass on my local machine just fine, many of them fail for no good reason on the CI server.

If you can avoid it, do; PowerMock is basically only for tests that interact with badly designed code that you can't change. But sometimes you need to depend on a badly designed library, in which case having a somewhat cumbersome way to mock it is better than nothing.
"PowerMock is basically only for tests that interact with badly designed code that you can't change"

Presumably where the definition of badly designed includes "uses static methods"? That's something that I hold true, and teach to other people as true, but I have started wondering about it. Whenever I ask why it's bad I get told "it makes testing hard". Well, Powermock can make testing it possible - "but powermock is only for badly designed code". What's badly designed code? And so it goes round.

Is it really that conceptually bad? Or is it just that our testing tools are insufficient, or that the language itself did not make provisions for testing all of its features? I've seen a lot of code recently that uses dependency injection purely for testing, and uses builders instead of constructors all over the place, and it feels like a lot of effort and complexity to avoid using PowerMock.

It's not just about testing, though that's where you tend to run into it first. Statics and constructors are both second-class citizens in Java: they can't conform to interfaces, so there's no way to separate interface and implementation when using them, no way to hide information, no way to invert control or any of the other principles of good design.

(Of course, Java was designed by people doing the best they could subject to particular constraints, and is now constrained by backwards compatibility. But other languages provide the same kind of features in better ways (or provide syntax that means builders and dependency injection are much less effort, depending on which way you look at it))

Pragmatically, good design is not the only concern; if you're not writing a library with binary-compatibility concerns, then IMO the syntactic overhead of builders is often a cost that outweighs the benefits. But the design costs of statics and constructors in Java are real enough, and there are very direct costs to using PowerMock (tests are harder to maintain and more brittle as they have to specify which classes they're rewriting, and it can conflict with other things that do bytecode manipulation e.g. test coverage tools).

> While the tests pass on my local machine just fine, many of them fail for no good reason on the CI server.

Offtopic: Time for start using Docker? :p

Our CI servers are already virtual machines. Or did you mean my local machine? We have a standard ecosystem that everyone uses, but there must be some difference between the CI server and our ecosystem. I simply haven't had the time to figure out what or why yet and it's better/easier to simply remove PowerMock where the tests are broken.
> there must be some difference between the CI server and our ecosystem

Well that's why I suggested using Docker: To ensure both environments are exactly the same.

You say they are virtual machines, but how do you provision them? I assume you are using puppet/chef/salt/ansible and creating them from scratch, as opposed to uploading whatever image you used in dev, like you would with Docker.

Fake, not Mock (if you need to Mock, you've alrrady lost), but yes, you shouldn't write anything static if you'll need to provide an alterate implementation of the interface.