Greetings, fellow Pythonistas! I'm about to create three modules. As an avid TDD fan I'd like to create typical 'use-cases' for each of these modules. One of them is rather large, and I wondered if it would be easy enough to create a code skeleton out of unit test module.
Consider the following, though contrived, unit test code snippet: ========================================================================== import player class TestClass: def setup_method(self, meth): self.obj = player.Player('Fred the Adventurer') def test_name(self): assert self.obj.name == 'Fred the Adventurer' def test_inventory(self): # player always starts with a dagger assert self.obj.inventory == {'dagger': [(1, 4)]} # dagger is an initial weapon assert self.obj.weapon_type == 'dagger' # add sword to player and wield it self.obj.add_item('sword', (1, 8)) # wield the first sword in the backbag self.obj.wield('sword') assert self.obj.weapon_type == 'sword' assert self.obj.inventory == {'dagger': [(1, 4)], 'sword': [(1, 8)] } def test_level(self): cur_level = self.obj.level self.obj.level_up() assert cur_level + 1 = self.obj.level def test_hitpoints(self): start_hp = 30 sword_damage = 6 assert self.obj.hitpoints == start_hp self.obj.damage(sword_damage) assert self.obj.hitpoints == start_hp - sword_damage ========================================================================== Now it would be nice if I could do $ python test2skel.py test_player.py $ cat player.py class Player: def __init__(self, str): # constructor was called with a string, # so we create init with str arg # self.weapon_type was compared to string and no parenthesis were # used, so the best guess is it's an attribute self.weapon_type = None # ditto for inventory, name and hitpoints self.inventory = None self.name = None self.hitpoints = None def add_item(self, str, tpl): # add_item() was called with a string arg and a tuple pass def damage(self, obj): # damage() was called with an argument def wield(self, obj): # wield was called with an arg def level_up(self): # level_up() was called without args Now I'm thinking of at least three possible ways to implement that, all of which may not be very sensible, but what I could come up with: 1. Try to run the test case and capture exceptions. Trying to import player and failing means there is no module player.py. The script could create a file player.py. Running the tests again imply there's an attribute Player which seems like a method call or class (both are 'called' in the same way). Nothing is created, but an ambiguous state is set for Player: it's either a class or module method. Later calls for Player instance remove the ambiguity and then we know it's a class, and as such the class skeleton is created. Proceeding this way a skeleton could probably be created by catching Import/Attribute errors and so forth. 2. Parse the test cases module as Python code, and extract all possible information therein. Probably the best-working method, but needs Python parser and some serious semantic logic. Probably the most obvious but also hardest(?) way to do it. 3. Parse the test case as a text file, and extract only necessary information. Eg. creating needed module files is probably very easy: for practical purposes, you could just grep "import <modlist>" and "from <module> import <classes/method>" to find out what module files need to be created. Hackish and not elegant but would probably work for custom purposes well enough (especially if we restrict ourselves to only lines starting with 'import module' statements in the test module). However, eg. associating an instance method call with the class used in object creation would be quite hard, if the parser is just a simple one. 4. Use a completely different approach, eg. create a module skeleton using a custom markup (easily generated from many graphical design tools) and use that for creating both the test skeleton and module itself. However, it wouldn't be TDD because then you'd be designing the module the usual way Also remember that the program wouldn't need to be very clever. A strict coding standard can be assumed, and eg. the fact that all classes are in CamelCase would help in deducing whether foo.Bar() is a method call or Bar-class object instantiation (methods are always lower_case_with_underscore, according to PEP 8 we try to follow). What do you think? I know that probably the best way to go on is just do it the old way (ie. code both the test and module by hand), or look for more intelligent IDEs. For what it's worth, I still use most recent XEmacs for coding because it is just so handy in many areas - sometimes I miss Eclipse-like IDE and I have even tried pydev for Eclipse (which is a good plugin, btw!), but to this day I've went back to XEmacs every time because in the end I've always been lacking something I couldn't find a decent substitute for. Obligatory note: According to the XP folks, TDD is not as much about testing as it is about design: "Test Driven *Development*" or maybe even "Test Driven *Design*". The problem can be solved more easily if you design the module skeleton first, then the tests and then the logic for the skeleton - you would be creating tests before the code, but many people wouldn't regard it as TDD then. -- # Edvard Majakari Software Engineer # PGP PUBLIC KEY available Soli Deo Gloria! $_ = '456476617264204d616a616b6172692c20612043687269737469616e20'; print join('',map{chr hex}(split/(\w{2})/)),uc substr(crypt(60281449,'es'),2,4),"\n"; -- http://mail.python.org/mailman/listinfo/python-list