Example of how to CodeUnitTestFirst:
(see UnitTestExamplesAndGuidelines)
Let's say I've got some classes: RailwayTrack, Train, Engine, and EngineType. The first test that comes to my mind is to create a train consisting of an Engine only, putting it on some standard track, and accelerating it a little. Then we could see if the Train moves accordingly on the Track.
Now, this is a rather complicated thing to test. You need a default EngineType, an Engine, a default Track plus a Train.
Four classes at a time without code is a whole lot. Why do we need to use the Type Object pattern? When I use a pattern that heavy that early, it is because I'm guessing. I hate guessing. So, the first question is why so many classes.
It is really difficult to answer questions like this in the abstract. What are we writing, a train simulation? A model train control system? A DB control system?
Let's say we're writing a simulation- then you have to describe some situation or scenario. Collision avoidance? Animation? Manual control? Automatic control?
Let's take the scenario above and try to write a test -
s := Simulation new. e := s engine. t := s trackFrom: 0@0 to: 100@0. e track: t at: 0@0 facing: #east. e accelerateTo: 1. s wait: 10. self assert: e position = 10@0.So, I discovered an object we didn't have, a Simulation, and I didn't have two objects we used to (actually, there aren't any other classes, but I'm imagining Engine and Track). I did it by imagining how simple the system could possibly be from the perspective of the test, not the objects. Then I make the objects that the test needs. Nothing more, nothing less. Contrast this with what we did before, which seems to be to come up with the objects and try to fit the tests to the objects.
Try designing with the tests first a few times and report back.
This reminds me of the good technique of top down modeling where you first model your use cases as operations on a 'system' object. It makes you think about the top-level features required on the system without trying to think up an elaborate model to take responsibility of each use case. It also means you can use as much SmokeAndMirrors as required. The system object is like an ApplicationFacade? I guess.
Isn't this about writing user code first, i.e. the test script before trying to think up the server?
From the XpMailingList:
Ron Jeffries and Michael Feathers demonstrated this approach at the TestFirst BOF at OOPSLA (OopslaTwoThousand). The story was to create a point-of-sale (POS) system that displayed the name and price of a product when it was scanned. From memory, here's what the code looked like after a few tests had been written: --- test --- public void testGetProductCheerios() { Product expected = new Product("Cheerios", 3.95); assertEquals(expected, _inventory.getProduct("CH103")); } public void testGetProductCornflakes() { Product expected = new Product("Cornflakes", 4.95); assertEquals(expected, _inventory.getProduct("CO221")); } --- end test --- --- production --- [class Inventory] public Product getProduct(String upcCode) { Hashtable products = new Hashtable(); products.put("CH103", new Product("Cheerios", 3.95)); products.put("CO221", new Product("Cornflakes", 4.95)); return products.get(upcCode); } --- end production ---
This is a really illuminating example. I think my problem was not letting go of the database, since I knew I was going to need it later. I didn't realize I still didn't have to put it in.
So this is perhaps an argument for not thinking ahead even when you know where you are going: thinking ahead forces the step size to grow.
Asim
The key here is that Ron and Michael didn't make grand plans. They didn't say, "oh, we need a database to store this data." That just did the simplest thing that could possibly work -- they hard-coded the data necessary to get the tests to pass. When they evolved it to use a database, they again used a simple approach. They put the DB code right in the Inventory class. But as soon as another object needed to access the DB, they refactored it out.
Some of you may be wondering what would force you to evolve this to use a database. Here's a hint. The next stories were "Allow the store manager to change the names and prices of products." and "Preserve the latest product info even when the system goes down."
Jim