views:

215

answers:

5

Hi,

I often find myself changing my code to make it more testable, I always wonder whether this is a good idea or not. Some of the things I find myself doing are:

  1. Adding setters just so I can set an internal object to a mock.
  2. Adding getters for internal maps/lists so I can check the internal state of the object has changed after performing some external action.
  3. Wrapping concrete system classes and creating a new interface so I can mock them. For example, File classes can be hard to mock - so I'll create a new interface FileInterface and WrappedFile which extends it and then use the FileInterface instead of File.
A: 

Seems reasonable. Some things don't need checking though; I'd suggest checking to see if adding to a list worked is a little useless. But do whatever you feel comfortable with.

Noon Silk
+7  A: 

Changing your code to make it more testable can be a good thing, but only if it makes your code itself better. Refactoring for testability can make your code better independent of the test suite's needs. Those are good changes.

Of your three examples only #3 is a really good one; often those new interfaces will make your code more flexible for regular use later. #1 is usually addressed for testing via dependency injection, which in my mind makes code needlessly more complicated but does at least make it easier to test. #2 sounds like a bad idea in general.

Nelson
This sums it up perfectly. With respect to number 2, one of the hardest things for me when starting with TDD was to be convinced that I did not need to test private/internal logic -- only the public stuff that the rest of the application is actually going to consume.Once you get over that hurdle, it does make sense and is liberating.Dependency injection is an essential design shift to accomplish this with mocking.
Jay
I agree with your first paragraph, but not with the second.Creating separate interfaces up-front, to "make your code more flexible for regular use later", is just over-engineering.In my experience, the supposed extra flexibility tends to never get used. Code should be designed for use first, and for reuse second.
Rogerio
A: 

It's a trade-off..

You want a slim, streamlined API or a bloated more complicated, but easilier tested one.

Not the answer you wanted to hear, I know :)

cwap
Making code more testable usually leads to better, simpler code; not bloated or more complicated.
Samuel Carrijo
Certanly depends on how you make it easier to test. Also, which is simpler? Just instantiating an object, or adding a reference to some IOC-library, register your type, create an interface for it, and then ask that library for an instance of your object? I certainly agree that IOC and interfaces are nice, but there's also such a thing as "not reasonable" and "overkill"...
cwap
"Object" in the above should be read as "class" ofc ^^ I'm tired..
cwap
+5  A: 

It is perfectly fine and even recommended to change your code to make it more testable. Here is a list of 10 things that make code hard to test.

I think your third is really ok, but I'm not too fond of the first and the second. If you just open your class internals with getters and setters, then you're giving up encapsulation completely. Depending on your language, there are ways to open visibility of some parameters to test. But what I actually do (which opens encapsulation a little less) is to make the fields I want to check protected (when dependency injection doesn't solve the problem).

Then, on the test project, I inherit the class, and create a "more powerful one", where I can check the internals, but I change nothing on the implementation, and use this class in the tests.

Finally, changing your code to have dependency injection and inversion of control is also highly recommended, as it makes your code easier to test AND more readable and maintainable.

Though changing is ok, the best things to do is to TDD. It produces testable code naturally, once the tests are written first.

Samuel Carrijo
A: 

Ideally, every class you design should test itself, so you don't need to change the public interface. But you are talking about legacy code, so I think that changing code is reasonable only when the public impact isn't much noticeable. I would prefer to add a static inner class to test instead of bloat the interface of the tested class.

GogaRieger
This does not seam like a mainstream idea / approach. Would you like to elaborate, or provide some references?
Torbjørn