Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Depend on Concrete Classes - Tie things down to concrete classes - avoid interfaces wherever possible.

Well, yes, if those classes are implementation details why would you want otherwise?

Just to write “unit” tests with mocks all over the place that later will be broken after the first refactor?



Wherever possible, I try to avoid using mocks as they just end up obscuring what the code is doing.

More specifically, I follow the maxim of only using in the tests what is publicly available to a user of the class. If it is a private instance of a class, or a class created in a function that does not have a mechanism to be switched for something else, you shouldn't be poking the internals to modify it, you should either:

1. provide a mechanism to change that class -- e.g. if the class under test is talking to a database;

2. not change the code -- e.g. if the class under test is using a particular data structure class.

You should not have/use mocks if you have a class A that holds and uses class B to perform its work. If you do, you are not properly testing class A's behaviour. That is, if you have a rotation helper class that uses a quaternion class, you shouldn't be mocking the quaternion class behaviour -- that way only leads to madness, expecially if the internal implementation of the rotation class changes its internal representation/logic to use matrices.

Acceptable places to use mocks or similar techniques are:

1. for the class that wraps the database object, or some other external system -- for which, I don't class the filesystem to be external;

2. for setting up parts of an application (e..g IntelliJ) when testing a plugin -- and there, I try to get as close to the application behaviour as possible/necessary to perform the tests (i.e. wherever possible, use the application's real implementation class).

3. for setting up things like Spring, or other dependency injection components.


One of the premises/promises of OO is the ability to substitute implementations (in particular, dynamically, but in a modular programming sense also statically). An over-reliance on concrete classes makes this harder to achieve, and (in the context of the list, which is about testing) means you have a larger portion of the program that has to be brought into the test harness to test something.

On the former, using concrete classes instead of interfaces can be worked around if they're still configurable, but now you have inappropriate (or potentially inappropriate) subclasses to compensate for the lack of an interface. On the latter, well, it makes testing harder which is the entire point of the list. If A has two components B and C which are both tied to concrete classes and not interfaces, then you have to bring A, B, and C into the test harness when you really just want A and (possibly) B or C or stubs/mocks for them.

If you don't care about testing (and, in particular, unit testing), and don't care about substitutability, then don't worry about it.


I care about testing and that’s why I hate to break my tests every time a refactor comes. If the behavior doesn’t change my tests shouldn’t break. Tests should meant to be there to give me confidence about the main functionality. They should tell me when my main functionality is broken or not after a change.

It’s not very useful to me if I get red tests just because the structure of my code changed. Those red tests don’t tell me if the functionality is the same or not. If my tests break every time even if the functionality is the same, then I start to ignore the warning, I just blindly make them pass. They become “The Boy Who Cried Wolf”.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: