Page 1 of 1
Testing the boundaries of a "replaceable" layer...
Posted: Tue May 02, 2006 2:05 pm
by nielsene
I've been running into some difficulties with driving the development of the exposed client API for a package I've been working on. I've gotten the core functionality of the module written and have started to refactor it; however I need to triangulate in on the correct form and wanted to start pushing some top-down mock driven tests into the core from the planned API to help meet the bottom-up tests so far.
However the module is designed to be very self contained and to a large extend self configuring. This makes it very hard to inject a mock into the system and I don't want to have the "Unit Tests" for this interface plumb all the way through the system into the database as that would limit their usefulness as unit tests.
Normally I'd take a dependency injection approach/refactoring to gain a place to inject the mock, but that would break the self-containedness of the module and require more application side work to set everything up. Another option, I think is to separate the client API to two layers, one that's the more exposed dependency injection friendly class and one that's only a constructor to handling the hook-ups and all the forwarding calls to expose to the clients.
Is there another option? How do people normally attack this situation?
Posted: Tue May 02, 2006 2:38 pm
by Christopher
I'd go throught the "code smells" list and see how I could break the module up into more testable units.
Posted: Tue May 02, 2006 2:55 pm
by nielsene
That still only reflects the "inside" and is what I'm already considering under the listed option. It still doesn't address how one would actually test the exposed interface, when the interface should be more black-box like.
Posted: Tue May 02, 2006 3:00 pm
by Christopher
I don't understand. Black boxes are what everything should be and you only test what goes in and out of the interface.
Posted: Tue May 02, 2006 3:08 pm
by nielsene
This API is designed to be rather opaque to the clients. Looks like a one-argument constructor and then some exposed function calls. The constructor includes some simple configuration data (DB connection info and one path).
This module should NOT receive the database handle/object directly as its responsible for opening the connection, etc. This is the top most client facing interface in an ORM. So from the outside I can't mock up the database object to shortcut the DB. There's a ton of other supporting classes, various hash maps, etc that are created and maintained from within the layer, that would be useful to mock while testing the external interface in order to better exercise the interface. However there's no hook, and should be no hook, from the outside.
The first option I've considered is to work one level down -- remove the facade that wraps the functionality. At this level I'll have the mock injection points; however it only postpones the problem. At some point I'll need to test the exposed API. It may be a trivial forwarding api, but it will need to be tested and I don't see how I'll do that.
Posted: Tue May 02, 2006 3:21 pm
by Christopher
Again, it sounds like you need to break things up. The top level Gateway class that creates the DB connection and other stuff should then call other classes and not be monolithic.
Posted: Tue May 02, 2006 3:32 pm
by nielsene
arborint wrote:Again, it sounds like you need to break things up. The top level Gateway class that creates the DB connection and other stuff should then call other classes and not be monolithic.
It does call other classes. The top level gateway is basically just a facade over complex (and broken up) internals. How does one test a facade?
Posted: Tue May 02, 2006 4:08 pm
by Christopher
If it has responsiblities then test those. I don't see how it is any different than any other class?
Posted: Tue May 16, 2006 3:54 pm
by Ambush Commander
Well, you don't want the facade to depend on the classes it works over. But if you remove that stuff, the class may be trivially simple.
In that case, you may not want to unit test the interface. If the interface does interesting things unrelated to the underlying classes, you'd probably be able to test those independently of the underlying classes. But otherwise, it's an interface, and that's not really what unit tests are for (in my opinion). What you can do, however, is document the interface.
I might be totally wrong, but that's my two cents.