Improved Testability Leads to Better Design

From WikiContent

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
-
''Economic Testability'' is a simple concept but not one often seen in practice. Essentially it boils down to recognizing that since tested code should be a requirement, and that we have in place some nice, economical, standard tools for testing (such as xUnit, Fit, etc.), then the products that we build should always satisfy the requirement of ease-of-test in addition to any other requirements. Happily this requirement reinforces rather than contradicts best practice. Perhaps a few examples will help?
+
''Economic Testability'' is a simple concept but not one often seen in practice. Essentially it boils down to recognizing that since tested code should be a requirement, and that we have in place some nice, economical, standard tools for testing (such as xUnit, Fit, etc.), then the products that we build should always satisfy the requirement of ease-of-test in addition to other requirements. Happily this requirement reinforces rather than contradicts best practice.
-
It is often the case that tests are impeded because certain major functions are not yet implemented. For example,an object needs to be tested but it makes use of a yet-to-be-built object is a pretty common occurrence. A way around this is to allow the provision of the yet-to-be-built object via a parameterized constructor. Then, in the test-case situation we can provide a dummy object without changing the internals at all; in the production model we can provide the real (tested) object to deliver the required functionality. When implemented via the constructor, as recommended, in the set-up region of the tests, the actual method signatures are not changed at all.
+
Progressive testing is often stymied because certain major functions have not yet been implemented. For example, an object needs to be tested but it makes use of a yet-to-be-built object is a pretty common occurrence. A way around this is to allow the provision of the yet-to-be-built object via a parameterized constructor: When testing we can provide a dummy object without changing the internals at all; in the production code we can provide the real (tested) object to deliver the required functionality. The actual method signatures are unchanged and the object under test is unaware of whether it is running in a test or a production environment.
-
We can go further of course .. a lot further. If we wanted to introduce some kind of logging-trail for complex bug diagnosis, then we can provide the logging logic via the technique outlined above, by providing the logging object in the constructor parameters. At production time, using the null-object pattern, we can replace the logger with a harmless alternative. On the other hand, why not make this dynamically selectable? Then we could have a production system which can gracefully slip into logging mode. It may be unfortunate that you should need to do this, but if you do, then you can look like a hero, i.e., a professional!
+
We can go further of course — a lot further. If we wanted to introduce some kind of logging trail for complex bug diagnosis, we can provide the logging logic via the plug-in technique outlined above, providing the logging object in the constructor parameters. In production, using the ''null object'' pattern, we can replace the logger with a harmless alternative. On the other hand, why not make this dynamically selectable? Then we would have a production system that could gracefully slip into logging mode. It may be unfortunate that you should need to do this, but if you do, then you can look like a hero, i.e., a professional!
-
Finally, if you build your systems so that testing is made simple, either by following the suggestion here, or by just making the interfaces very simple to use in the xUnit world, and simultaneously of course preserving simplicity in the production model, then our experience has been that the interfaces that you finally end up with are superior in terms of encapsulation and portability than if you had built you system with just one target environment in mind. In other words, the discipline in building an interface which is equally at home in two environments causes you to do a better job in the design-essentials than otherwise. It really is a win-win situation.
+
If you build your systems so that testing is made simple — while simultaneously of course preserving simplicity in the production model — the interfaces that you finally end up with is likely to be greatly improved. It is superior in terms of encapsulation and portability than if you had built you system with just one target environment in mind. The discipline involved in creating an interface that is equally at home in two environments causes you to do a better job in the design essentials than otherwise. It really is a win–win situation.
By [[George Brooke]]
By [[George Brooke]]

Revision as of 13:59, 30 July 2009

Economic Testability is a simple concept but not one often seen in practice. Essentially it boils down to recognizing that since tested code should be a requirement, and that we have in place some nice, economical, standard tools for testing (such as xUnit, Fit, etc.), then the products that we build should always satisfy the requirement of ease-of-test in addition to other requirements. Happily this requirement reinforces rather than contradicts best practice.

Progressive testing is often stymied because certain major functions have not yet been implemented. For example, an object needs to be tested but it makes use of a yet-to-be-built object is a pretty common occurrence. A way around this is to allow the provision of the yet-to-be-built object via a parameterized constructor: When testing we can provide a dummy object without changing the internals at all; in the production code we can provide the real (tested) object to deliver the required functionality. The actual method signatures are unchanged and the object under test is unaware of whether it is running in a test or a production environment.

We can go further of course — a lot further. If we wanted to introduce some kind of logging trail for complex bug diagnosis, we can provide the logging logic via the plug-in technique outlined above, providing the logging object in the constructor parameters. In production, using the null object pattern, we can replace the logger with a harmless alternative. On the other hand, why not make this dynamically selectable? Then we would have a production system that could gracefully slip into logging mode. It may be unfortunate that you should need to do this, but if you do, then you can look like a hero, i.e., a professional!

If you build your systems so that testing is made simple — while simultaneously of course preserving simplicity in the production model — the interfaces that you finally end up with is likely to be greatly improved. It is superior in terms of encapsulation and portability than if you had built you system with just one target environment in mind. The discipline involved in creating an interface that is equally at home in two environments causes you to do a better job in the design essentials than otherwise. It really is a win–win situation.

By George Brooke

This work is licensed under a Creative Commons Attribution 3

Back to 97 Things Every Programmer Should Know home page

Personal tools