Saturday, May 29, 2010

Python mock testing with ludibrio module

In a unit test, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. You can install ludibrio mock framework module issuing the following command:
easy_install ludibrio
This framework comes with:
  • Mock - recorded expectations, strict behaviour
  • Dummy - never validated, ignore behaviour
  • Stub - canned answers, loose behaviour, give me as much as you can or Dummy
  • Spy - proxy, forwarder behaviour, give what you setup, everything else from wrapped type
Suppose you need to test withdraw operation in ATM (file atm.py):
from datetime import datetime

class Atm:
    def signin(self, account):
        self._account = account

    def signout(self):
        self._account = None

    def withdraw(self, amount):
        try:
            self._account.withdraw(amount)
            self._account.comission(amount * 0.005)
        except ValueError:
            self._account.comission(amount * 0.001)
        return self._account.balance(datetime.now())
Here is our test (file ludibrioexample.py):
from atm import Atm
import unittest
from ludibrio import Mock, Dummy

class TestAtm(unittest.TestCase):

    def setUp(self):
        self._mock_account = Mock()
        self._atm = Atm()
        self._atm.signin(self._mock_account)

    def tearDown(self):
        self._atm.signout()
        self._mock_account.validate()

    def test_withdraw(self):
        # Arrange
        with self._mock_account as a:
            a.deposit(150) >> None
            a.withdraw(100) >> None
            a.comission(0.5) >> None
            a.balance(Dummy()) >> 49.5

        # Act
        self._mock_account.deposit(150)
        remaining_balance = self._atm.withdraw(100)

        # Assert
        assert remaining_balance == 49.5

    def test_withdraw_insufficient_funds(self):
        # Arrange
        with self._mock_account as a:
            a.deposit(50) >> None
            a.withdraw(100) >> ValueError('Insufficient funds')
            a.comission(0.1) >> None
            a.balance(Dummy()) >> 49.9

        # Act
        self._mock_account.deposit(50)
        remaining_balance = self._atm.withdraw(100)

        # Assert
        assert remaining_balance == 49.9

if __name__ == '__main__':
    unittest.main()
Run tests:
python ludibrioexample.py
Read more about ludibrio here.

No comments :

Post a Comment