Code correctness, and testing strategies

David wizzardx at gmail.com
Sun May 25 13:36:06 EDT 2008


Hi again.

Taking the advice of numerous posters, I've been studying BDD further.

I spent a while looking for a Python library which implemented BDD in
Python similar to jbehave, as described by Dan North on this page:
http://dannorth.net/introducing-bdd. I did find a few, but they either
had awful-looking syntax, or they were overly-complicated. So I
decided that using regular unit tests (via nosetest) was good enough,
even if it doesn't have support for stories, scenarios, givens, etc,
and it uses words with "Test" in it instead of "Behavior".

One thing I just tried was to put together a basic stack class
following BDD, with nosetest. I got the idea from this page:
http://www.ibm.com/developerworks/web/library/j-cq09187/index.html

It was an interesting exercise, and I'm encouraged to try it further.

I ended up with these 2 modules:

======test_stack.py========

from nose.tools import raises
import stack

class TestStackBehaviour:
    def setup(self):
        self.stack = stack.Stack()
    @raises(stack.Empty)
    def test_should_throw_exception_upon_pop_without_push(self):
        self.stack.pop()
    def test_should_pop_pushed_value(self):
        self.stack.push(12345)
        assert self.stack.pop() == 12345
    def test_should_pop_second_pushed_value_first(self):
        self.stack.push(1)
        self.stack.push(2)
        assert self.stack.pop() == 2
    def test_should_leave_value_on_stack_after_peep(self):
        self.stack.push(999)
        assert self.stack.peep() == 999
        assert self.stack.pop() == 999
    def test_should_pop_values_in_reverse_order_of_push(self):
        self.stack.push(1)
        self.stack.push(2)
        self.stack.push(3)
        assert self.stack.pop() == 3
        assert self.stack.pop() == 2
        assert self.stack.pop() == 1
    @raises(stack.Empty)
    def test_peep_should_fail_when_stack_is_empty(self):
        self.stack.peep()
    def test_should_be_empty_when_new(self):
        assert len(self.stack) == 0

======stack.py========

class Empty(Exception):
    """Thrown when a stack operation is impossible because it is empty"""
    pass

class Stack:
    """Basic implementation of a stack"""
    def __init__(self):
        self._data = []
    def push(self, value):
        """Push an element onto a stack"""
        self._data.append(value)
    def pop(self):
        """Pop an element off a stack"""
        try:
            return self._data.pop()
        except IndexError:
            raise Empty
    def peep(self):
        """Return the top-most element of the stack"""
        try:
            return self._data[-1]
        except IndexError:
            raise Empty
    def __len__(self):
        """Return the number of elements in the stack"""
        return len(self._data)

===================

Does the above look like a decent BDD-developed class?

Is it ok that there are no 'scenarios', 'stories', 'having', 'given',
etc references?

Some pages suggest that you should use so-called contexts
(EmptyStackContext, StackWithOneElementContext, FullStackContext,
AlmostFullStackContext, etc).

Would you normally start with a basic TestStackBehavoiur class, and
when Stack becomes more complicated, split the tests up into
TestEmptyStackContext, TestStackWithOneElementContext, etc?

Another thing I noticed is that some of my test cases were redundant.
Would you normally leave in the redundant tests, or remove the ones
which are included in the more general test?

Also, I have another question. How do you unit test event loops?

eg: Your app is a (very basic) service, and you want to add some
functionality (following BDD principles)

Here's an example unit test:

class TestServiceBehavior:
    def setup(self):
       ...
    def test_handles_event_xyz(self):
       ...

If your service is normally single-threaded, would your unit test need
to start the service in a separate thread to test it?

Another method would be to update the event loop to enable unit
testing. eg only iterate once if a 'being_tested' variable is set
somewhere.

None of the above are ideal. What is a good way to unit test event loops?

David.



More information about the Python-list mailing list