Creating module skeleton from unit tests

P

Peter Maas

Edvard said:
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.

I think this is too difficult, because there are many ways to write
code (even skeletons) for a use case. An easier approach would
be to write the skeleton manually, embed the test cases in the doc
strings and generate the test code from the doc strings. If I
remember correctly IBM has published something to generate unit
tests from code. Python has a doctest module to support testing
derived from doc strings. This can be combined with unit tests.
> 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.

You shouldn't care if your approach works for you.
 
E

Edvard Majakari

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";
 
F

Fabio Zadrozny

I think that the best approach I saw to this was in the Eclipse java
ide... You can basically go on the declaration of

self.obj = player.Player('Fred the Adventurer')

press Ctrl+1 and it adds a suggestion to create the class Player.

Then go to

assert self.obj.name == 'Fred the Adventurer'

press Ctrl+1 and it adds suggestion: Declare field name in class
Player... and so on for methods... (true, you still have to go and
press some Ctrl+1s, but that should be fairly easy, especially if you
had some hints on what is missing... Python has a very dynamic nature,
but most of it can still be done...

I think that most Python IDEs are still not in the same level, but some
day they might get there...
Being the maintaner of PyDev (http://pydev.sf.net), I think it will get
there someday, true, lots of work to make it happen, right now only few
things in Ctrl+1 are available like that (still, some already are)...
and that's the way things work... nothing's always perfect (but at least
they evolve).

Regards,

Fabio
 
E

Edvard Majakari

Peter Maas said:
I think this is too difficult, because there are many ways to write
code (even skeletons) for a use case. An easier approach would
be to write the skeleton manually, embed the test cases in the doc
strings and generate the test code from the doc strings. If I
remember correctly IBM has published something to generate unit
tests from code. Python has a doctest module to support testing
derived from doc strings. This can be combined with unit tests.

Yes - actually I channged my mind in somewhere in the article - I actually
don't want to create typical use-cases in the test bed, rather create a
skeleton which covers each method at least somehow (no code inside skeleton
would be needed).
You shouldn't care if your approach works for you.

Yes, you are right. I didn't select my words very well - I just meant that it
wouldn't be TDD then. Of course it might work, but I'd like to to it the TDD
way for now.

But thanks for the tip, I could see what IBM has done and then forget about
doing it automatically :)

--
# 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";
 
E

Edvard Majakari

Fabio Zadrozny said:
I think that the best approach I saw to this was in the Eclipse java
ide... You can basically go on the declaration of

self.obj = player.Player('Fred the Adventurer')

press Ctrl+1 and it adds a suggestion to create the class Player.

Then go to

assert self.obj.name == 'Fred the Adventurer'

press Ctrl+1 and it adds suggestion: Declare field name in class
Player... and so on for methods... (true, you still have to go and press
some Ctrl+1s, but that should be fairly easy, especially if you had some
hints on what is missing... Python has a very dynamic nature, but most of it
can still be done...

Yes, I know. Eclipse is an excellent Java IDE - I've already seen ''quick
assist'' at work and I envy those Eclipse users for having such a nice tool :)

PyDev for eclipse seems /very/ promising, though.
I think that most Python IDEs are still not in the same level, but some day
they might get there... Being the maintaner of PyDev (http://pydev.sf.net),
I think it will get there someday, true, lots of work to make it happen,
right now only few things in Ctrl+1 are available like that (still, some
already are)... and that's the way things work... nothing's always perfect
(but at least they evolve).

Thanks for the comments - and for your pydev plugin, too.

--
# Edvard Majakari Software Engineer
# PGP PUBLIC KEY available Soli Deo Gloria!

"Debugging is twice as hard as writing the code in the firstplace. Therefore,
if you write the code as cleverly as possible, you are, by definition,
not smart enough to debug it." -- Brian W. Kernighan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top