[erlang-questions] Unit testing code - whence?
Wed Jul 6 21:55:39 CEST 2011
I think it's important to first define the complexity of a module. You have some very simple modules (or rather, simple in principle) with a functional interface. These can often be unit tested from the outside only, without bothering too much with the rest.
In the case of these 'simple' modules, one of the problems with the tests that look at the innards of the module is that they can quickly become a maintenance overhead, and are also frequently useless in the face of refactoring -- you need to drop these tests, write new ones, etc. I find that I tend to rely on the interface I've established for all of these, instead.
TDD done starting from the outside also has the advantage of having you thinking as the user of the code rather than the writer of it; a rather important point in design, I'd say.
In the case of more complex modules, like gen_servers, which actually hide state and give you a destructive interface, I first tended to test things from the inside, but quickly decided that it was much simpler for me to test things from the outside, while cheating and using 'sys:get_status/1' to get a peek at the inside state of the server. That way, the whole thing remains more authentic to its runtime environment while still allowing you to do white box testing. It's also much simpler when you get side-effecting functions as part of the code itself (trying to send exit signals, links, gen_server:reply, etc.)
In the case of even more complex processes, like FSMs with complex states where you don't want to repeat sequences of events leading to a given state, you can use gen_fsm:enter_loop/4 (http://www.erlang.org/documentation/doc-5.7.1/lib/stdlib-1.16.1/doc/html/gen_fsm.html#enter_loop-4) with a known state to observe the transitions and replies you need, although I've rarely done that myself, given how dirty it feels. I'll rather build the whole sequence in a setup function as it will also act as documentation for those who want to use that.
Most of the time, it doesn't exactly matter to me where the limit is between unit and integration and system and etc. I usually just want to make sure things are tested right, and in the way that will bring me the least maintenance overhead possible when re-working code in a few months/years. Your mileage may vary depending on what kind of projects or within what kind of team you're working, though.
On 2011-07-06, at 15:32 PM, Jon Watte wrote:
> Actually, test driven development methodology has a very clear separation between these:
> - acceptance tests / interface tests use only the public, exported interface of a component/system/unit, and tests it in a "clean room" environment.
> - unit tests grope around the internals of your unit in a way that nobody on the outside can. It's used to verify implementation details of the unit that may be important to the implementation, but not to anyone on the outside.
> There is a little bit of religion in this -- some people tend to feel very strongly that only acceptance tests matter. Personally, I find unit tests to be useful in many cases, especially when growing an implementation using testing in the first place.
> For example: You might want to test a gen_server. This gen_server uses a state record that is internal to the module. A unit test, inside the module, can set up a state record, and then call handle_call() directly, making sure that particular implementation details do what they're supposed to. Meanwhile, nobody outside the module should ever call that function (except for the gen_server itself, of course), and any test on the outside should only use the public interface functions.
> Americans might object: there is no way we would sacrifice our living standards for the benefit of people in the rest of the world. Nevertheless, whether we get there willingly or not, we shall soon have lower consumption rates, because our present rates are unsustainable.
> On Tue, Jul 5, 2011 at 10:56 PM, Gianfranco Alongi <gianfranco.alongi@REDACTED> wrote:
> I disagree.
> Putting your tests in another module will actually force you to think more of your design, making the application/lib more testable without being necessarily more "open". Another huge win is that you will have a cleaner implementation module. And you will be able to easily mix and match revisions of code and tests. /G
> erlang-questions mailing list
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the erlang-questions