This approach works well, up to a point. However, in a large system, and particularly one where the underlying functionality is complex, it becomes very difficult to test everything through external interfaces. A simple technique is to make the code do its own self-testing, using assertions. If the statement is false, the assertion stops processing to allow debugging. Of course, in the release version of the system, these tests are redundant and waste processing time.
Assertions are particularly useful for testing preconditions, postconditions, and class invariants.
While self-validation helps with problems which may not be found quickly during system testing, it does not help at all for the parts of the code which system testing does not reach. For this, we must produce separate code explicitly to exercise the functionality. This is code-level testing. Given the ease of debugging Smalltalk code, and of executing statements on the fly, indiscriminate code-level testing is a waste of time. To optimise the effect of testing, we need to be selective.