The idea of this note - as a hypothesis - appeared quite a long time ago, and somehow everything did not work ... But the other day (at the time of publication - weeks already) saw confirmation of his suggestion of what is called "first-hand" (see
Kent Beck's Testing the fixture originating from JUnit? ) and decided to implement this idea.
This is not about TDD itself, but rather just about the first steps in this direction. But, I think, the knowledge of the origins and understanding of the logic of the creators is an important moment in mastering what has grown in the field they sowed ... And in this case too.
So ... Once upon a time, a certain
Kent by the name of Beck and his friend
Ward were engaged in programming in the Smalltalk environment. I don’t know what tasks they were solving - and this is not so important - but they did it in such a way that even today you can indulge (with very few exceptions) only in the environments of this family. The fact is that in Smalltalk there is absolutely no gap between the writing of the program and its execution. And so you can invent the code on the go, immediately write and execute it. Moreover, it can be performed, however strange it may sound, even before writing ... And this is not a fairy tale - I can show how it looks in practice.
')
The first Smalltalk tool we need is called Workspace. By and large, this is a primitive (if not stronger) text editor. The only thing that the Workspace stands out in a long line of text editors is the ability to accomplish what is written. (There is a similar tool, for example, in llpse, it is called Display. It differs, except for any trifles, for the worse, by the inability to execute code without a running program, which, however, is not the fault of this tool, but rather the trouble of all systems with "crooked static typing.) This is how the Workspace looks in Smalltalk:

As you can see in the context menu, you can simply execute a line (or selected text), you can print the result, or you can view it in one of two inspectors available in this Smalltalk environment, etc.
“All this is wonderful, but how does this relate to unit tests?” The impatient reader will very reasonably ask. To answer, consider some simple task. Suppose, for example, we want to make a partner from the Smalltalk environment for the game
“Bulls and Cows” . Let us leave aside the excesses in the form of a specialized graphical interface and try to make it as simple as possible. The same Workspace is quite suitable for this: we ask the system to first create the object of the game, then send him messages with our variant of the guess, and the game returns a hint (the number of bulls and cows) ... for example, in the form of a dot: for example,
2 @ 3
(object
2
the message
@
with parameter
3
is sent - as a result we get an instance of the class
Point
, where
x = 2
,
y = 3
) means two bulls and three cows;
4 @ 0
means that the key has been unraveled.
The general plan is ready, we start its implementation. You can develop it in the usual way: by creating classes, methods in them, etc. But you can do it differently - just start playing:

We expect that in response to this, the system will create an object of the class we need. To verify this, we can inspect the resulting object. Select the menu item Inspect It ... and get a warning system: she does not know what should be understood by the name BullsAndCows.

In general, Smalltalk is very friendly towards its user. For example, in this situation, this is manifested in the fact that the process of compiling the code (namely, at this stage we have stopped) in the event of a misunderstanding (the language does not turn to call it an error) does not end. The system only suspends the process, offering the user ways to resolve the problem. In this case, we are interested in creating a new class ("define new class")

In the proposed “template” (which is in fact an expression in the Smalltalk language that ensures the creation of a new class), it is advisable only to specify the category (package) in which the created class will be placed — let's call it plainly: “BullsAndCows”.

Click OK ... and see the opened inspector window with the created instance of the game.

We got what we wanted. Next step: letting the game know our version.
game guess: ???
Here we have to think: how best to imagine a hunch? Most likely, the same as the key ... but we haven’t yet remembered the key ... I have three options “automatically”: 1) get a special class for the guess, or 2) use a number, or 3) use a string. To choose, you have to think ... I stop my choice on the line, because (looking ahead) I understand that in the future I will have to match the key and the answer, and character by character, but the line is an indexed collection of characters.

Executing the second line results in an error.

According to the message in the window header, we see that the instance of the BullsAndCows class simply does not know how to react to the received message. Under the heading, possible options for further actions are presented: continue (ignoring the error), interrupt, debug, or create. As you can see, interruption of execution is not the only option in this case. You can continue, but this will lead to nothing - the method itself will not appear. There is no sense in debugging in this case either - everything is clear here: you need to create an appropriate method.

The system is interested in which class to create this method (in BullsAndCows itself or somewhere higher in the hierarchy?). We need the first option. Since it is customary to structure the set of object methods in Smalltalk, attributing the methods to different categories, the system will offer to do this.

Among the proposed standard categories, I liked testing. The method is created and opened in the debugger for editing.

Note that the exception did not lead to unwinding of the call stack (it is shown in the upper part of the window) and we will be able to continue executing the program as soon as we want. But first, let's set the implementation for #guess :. But for this we will have to decide on the issue with which, in fact, it was worth starting: what should our game answer in this particular case? I propose to pretend that the user didn’t guess anything at all: we’ll return him “0 bulls and 0 cows”. We implement as simple as possible (Fake It):

In order for the changes in the method code to be compiled, they must be “accepted” (Accept). After that, click the Proceed button to continue the execution of our game session ... And we get the expected result.

I hope that the principle has already become clear here ... At the next iterations, putting forward more stringent requirements for the behavior of our system, we will gradually develop it: modify (complicate) the methods created, add new ones if necessary, introduce new classes, etc. But each step will begin with the fact that we think through the correct behavior of the currently developed part of the system, highlight its "signs" - the expected results - and those actions that must be performed to obtain these expected results. Then we simply write down these actions and start the execution of the received program, without worrying about the presence of even the basic infrastructure for its work. By any means we force the written program to work as we want. If necessary, improve it. The signal to complete this iteration is the coincidence of real results with the expected ones and our satisfaction with the received code.
Before SUnit, there was a small step: to keep the expected result “in the head” every time is not profitable - in vain the already limited resources of human intelligence are wasted. There is a natural desire to write them down somewhere, from which it follows the idea that you can write right here in the Workspace. This also adds the desire to automate the process of comparing the obtained results with the expected ones, to save all the already developed options for using the system with all the checks and then to use them to exclude regression ... The requirements for the framework are almost ready. Then we implement them in the simplest way - this is what Kent Beck writes (see the link to the source above):
“When I started designing the first version of xUnit, I used one of my usual tricks: turn something into objects; in this case, the whole Workspace turned into a class. Each fragment would then be presented as a method (with the prefix „test“ as a primitive annotation). ”
... Then follows one of the most important discoveries in the field of software development, which at this moment already lies on the surface: the process of formalizing the requirements and obtaining the initial design of the system for them almost one to one coincides with the process of writing automatic tests. And this is already the basis of TDD: it remains only to summarize the experience gained, streamline the practice of writing control tests before implementing the functional, analyze your experience and make sure that there are several basic template techniques ... and the TDD methodology in its “classic” form is ready.
***
Instead of a conclusion, a little criticism. I got Beck and later architecture replicated everywhere. Instead of test methods, it would be convenient to have tests in the form of separate objects, which are necessaryly interconnected and controlled by the appropriate IDE tools. This approach can be as natural and convenient as possible in a dynamic, lively environment such as the Smalltalk. ... In general, this is a topic for individual articles - with preliminary research and development. And the starting point for them is the conclusion that xUnit and its clones, which are now widely used, are only the first approximation to solving the problem of using tests for developing software systems - so what is the result?