Rewriting code to drop items

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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s