Getting started with Modipyd and Autotest tool

You can read the How to install Modipyd document, if you don’t install Modipyd yet.

Basic Example: The Widget class

Suppose you have two .py files:

[~/modipyd/examples/widget]
% ls
test_widget.py        widget.py

The file named widget.py is a normal Python script. On the other hand, test_widget.py is a test case includes unittest.TestCase subclass.

widget.py:

import os
import sys

class Widget(object):
    """
    Widget is a User Interface (UI) component object. A widget
    object claims a rectagular region of its content, is responsible
    for all drawing within that region.
    """

    def __init__(self, name, width=50, height=50):
        self.name = name
        self.width = width
        self.height = height

    def size(self):
        return (self.width, self.height)
        

test_widget.py:

import unittest
from widget import Widget

class SimpleWidgetTestCase(unittest.TestCase):

    def test_init(self):
        widget = Widget('The widget', 10, 10)
        self.failUnless(widget.size() == (10, 10),
                        'incorrect size')

    def test_default_size(self):
        widget = Widget('The widget')
        self.failUnless(widget.size() == (50, 50),
                        'incorrect default size')

if __name__ == '__main__':
    unittest.main()

Running pyautotest

You can start monitoring changes in these files by executing pyautotest (With -v/--verbose option flag, pyautotest prints detailed information about its operations):

% pyautotest -v
[INFO] Loading plugin: <class 'modipyd.application.plugins.Autotest'>
[INFO] Loading BytecodeProcesser 'modipyd.bytecode.ImportProcessor'
[INFO] Loading BytecodeProcesser 'modipyd.bytecode.ClassDefinitionProcessor'
[INFO] Monitoring:
test_widget: test_widget.py
  Dependencies: ['widget']
  Reverse: []
widget: widget.py
  Dependencies: []
  Reverse: ['test_widget']

As output message mentioned above, the pyautotest tool is now monitoring widget.py and test_widget.py.

Refactoring: Assigning multiple values at once in intialization

You have three assignment statements in Widget.__init__(), assigns function arguments to instance variables. You can perform multiple assignment using tuples.

widget.py:

import os
import sys

class Widget(object):
    """
    Widget is a User Interface (UI) component object. A widget
    object claims a rectagular region of its content, is responsible
    for all drawing within that region.
    """

    def __init__(self, name, width=50, height=50):
        self.name = name
        self.width, self.height = width, height

    def size(self):
        return (self.width, self.height)
        

When you edit widget.py, pyautotest automatically reloads modified module (widget.py), and then, automatically runs all dependent testcase (test_widget.py).

Note

pyautotest makes no assumptions about filename of test cases, you can have widget_test.py or WidgetTest.py, test/widget.py, ... etc.

[INFO] Reload module descriptor 'widget' at widget.py
[INFO] Modified: widget: widget.py
  Dependencies: []
  Reverse: ['test_widget']
[INFO] -> Affected: widget
[INFO] -> Affected: test_widget
[INFO] Running UnitTests: test_widget
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Adding resize() method

You decide to add Widget.resize() method. The Widget.resize() takes two arguments width and height, then change the region with new rectangle. Along with test driven development, you write a test before the Widget.resize() implementation.

test_widget.py:

import unittest
from widget import Widget

class SimpleWidgetTestCase(unittest.TestCase):

    def test_init(self):
        widget = Widget('The widget', 10, 10)
        self.failUnless(widget.size() == (10, 10),
                        'incorrect size')

    def test_default_size(self):
        widget = Widget('The widget')
        self.failUnless(widget.size() == (50, 50),
                        'incorrect default size')

class WidgetResizeTestCase(unittest.TestCase):

    def test_resize(self):
        widget = Widget('The widget', 15, 30)
        self.failUnless(widget.size() == (15, 30))
        widget.resize(45, 50)
        self.failUnless(widget.size() == (45, 50))


if __name__ == '__main__':
    unittest.main()

The test must inevitably fail because Widget.resize() is missing. This validates that the test harness is working correctly and that the test does not mistakenly pass without requiring any new code.

Note

pyautotest automatically finds new unittest.TestCase subclass and its dependencies.

======================================================================
ERROR: test_resize (test_widget.WidgetResizeTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_widget.py", line 21, in test_resize
    widget.resize(45, 50)
AttributeError: 'Widget' object has no attribute 'resize'

----------------------------------------------------------------------
Ran 3 tests in 0.001s

So, let’s begin to write Widget.resize() code.

widget.py:

import os
import sys

class Widget(object):
    """
    Widget is a User Interface (UI) component object. A widget
    object claims a rectagular region of its content, is responsible
    for all drawing within that region.
    """

    def __init__(self, name, width=50, height=50):
        self.name = name
        self.resize(width, height)

    def size(self):
        return (self.width, self.height)

    def resize(self, width, height):
        self.width, self.height = width, height

Now all test cases pass.

[INFO] Running UnitTests: test_widget
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK