Long ago (like, last year), I wrote code that allowed characters to drop items they are carrying. That was before I came up with the new action system though and I never got around to actually update that corner of the code. Recently I started working with a new user interface written with PyQt4 and decided to upgrade drop code as a part of writing new inventory system.
I started writing a simple BDD-test with Cutesy. Nothing too fancy, but enough to test that the goblin can drop item it is carrying:
def test_dropping_item(self): """ Items dropped by character should end on the floor """ dagger = Dagger() Uglak = Goblin(carrying(dagger)) place(Uglak, middle_of(Level())) make(Uglak, drop(dagger)) assert_that(Uglak, has_dropped(dagger))
Few new words had to be defined for this to work: carrying, drop and has_dropped. Obviously running the test failed and I had to implement some code to get things working. However, instead of using BDD-test to guide me through writing the code, I used it only as a definition of goal. Now I had place to start (character with item, who can not drop it) and goal (character, who has dropped an item). Small steps that would take me there, I defined as regular unit tests (some are shown below).
class TestDropAction(object): """ Tests for dropping item """ def __init__(self): """ Default constructor """ super(TestDropAction, self).__init__() self.item = None self.level = None self.character = None self.action_factory = None def setup(self): """ Setup test case """ self.level = LevelBuilder().build() self.item = ItemBuilder().build() self.character = (CharacterBuilder() .with_item(self.item) .with_level(self.level) .build()) self.action_factory = (ActionFactoryBuilder() .with_inventory_factory() .build()) def test_dropped_item_is_removed_from_inventory(self): """ Test that dropped item is removed from inventory """ self.character.drop_item(self.item, self.action_factory) assert_that(self.item, is_not(is_in(self.character.inventory))) def test_dropped_item_is_added_on_level(self): """ Test that dropped item ends up on level """ self.character.drop_item(self.item, self.action_factory) assert_that(self.item.level, is_(equal_to(self.level)))
This is my preferred way of doing test driven development. Have one high level test to define your goal, that can be used to communicate with business owners. Have many low level tests that define technical implementation and guide you from start to finish in small steps. When something fails in the system later, we probably should end up with two errors: first one tells that dropping items is not possible anymore, second one tells that dingusX is broken and returns 1 instead of 2. Again, first one can be used to show general idea about the problem (it’s easier to talk with domain terms than with technical terms), while second one is tool for developers to pinpoint faul point.