Testing the boundaries of a "replaceable" layer...
Moderator: General Moderators
Testing the boundaries of a "replaceable" layer...
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?
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?
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
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.
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.
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
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?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.
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
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.
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.