Permalink
Browse files

Implemented will_raise and times modifiers

  • Loading branch information...
1 parent 0eeb89a commit 84d4e29f7bccb8d8c219f1ef84b1329c99b97da2 paolo_local committed Feb 14, 2012
Showing with 189 additions and 34 deletions.
  1. +30 −2 README
  2. BIN __init__.pyc
  3. +37 −9 mocks.py
  4. BIN mocks.pyc
  5. +122 −23 tests.py
  6. BIN tests.pyc
View
32 README
@@ -7,12 +7,40 @@ The goal is to be able to write something like this:
return a + b
# 2 + 2 = 4? Not for long!
- mock = mockacino.createMock(Calc)
+ mock = mockaccino.createMock(Calc)
mock.sum(2, 2).will_return(5)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
assert mock.sum(2, 2) == 5 # Take that, math!
It's a work in progress.
+
+Changelog
+
+Feb142012 - 11AM
+
+- Implemented times and will_raise method modifier
+
+ Now you should be able to do something like:
+
+ mock = mockaccino.createMock(self.MockedClass)
+ mock.method_that_returns_an_int().will_raise(ValueError)
+ mockaccino.replay(mock)
+ mock.method_that_returns_an_int()
+
+ And it will raise a ValueError or
+
+ mock = mockaccino.createMock(self.MockedClass)
+ mock.method_that_returns_an_int().times(2)
+ mockaccino.replay(mock)
+ mock.method_that_returns_an_int()
+ mock.method_that_returns_an_int()
+ mock.method_that_returns_an_int()
+
+ And it will raise an UnexpectedCall error on the third call.
+
+ The nexts steps are adding more tests, value checks to the will_raise
+ and times methods to increase robustness, add an will_return modifier.
+ The last one pretty likely will require a mass refactoring. Oops.
View
Binary file not shown.
View
@@ -45,11 +45,32 @@ class MockMethod(object):
'''
def __init__(self, name, parent):
- self.__name = name
+ self.name = name
self.__parent = parent
+ self.__num_calls = 1
+ self.__to_raise = None
+
def __call__(self, *args, **kwargs):
- self.__parent._invoked(self.__name, args, kwargs)
+ if self.__num_calls > 0:
+ self.__num_calls -= 1
+
+ return self.__parent._invoked(self, args, kwargs)
+
+ def is_depleted(self):
+ return self.__num_calls == 0
+
+ def times(self, num_expected_calls):
+ self.__num_calls = num_expected_calls
+
+ def will_raise(self, error):
+ self.__to_raise = error
+
+ def should_raise(self):
+ return self.__to_raise is not None
+
+ def do_raise(self):
+ raise self.__to_raise
class Mock(object):
@@ -60,25 +81,32 @@ def __init__(self):
def enter_replay_mode(self):
self.replay_mode = True
- def _invoked(self, method_name, args, kwargs):
+ def _invoked(self, mock_method, args, kwargs):
'''
Method called by a mock's methods when they are invoked. It checks
the method's name, its arguments and keyword arguments. The order
of arguments does matter, the order of keyword arguments doesn't.
'''
-
if self.replay_mode:
if not self.__calls:
raise UnexpectedCallError("No more method calls are expected")
- expected_name, expected_args, expected_kwargs = self.__calls.pop()
+ expected_name, expected_args, expected_kwargs = self.__calls[0]
+
+ if mock_method.is_depleted():
+ del self.__calls[0]
- if (method_name != expected_name or args != expected_args or
+ if (mock_method.name != expected_name or args != expected_args or
kwargs != expected_kwargs):
raise UnexpectedCallError("Unexpected method call. Expected \
%s(args=%s, kwargs=%s], got %s(args=%s, kwargs=%s)." %
(expected_name, expected_args.__str__,
- expected_kwargs.__str__, method_name, args.__str__,
- kwargs.__str__))
+ expected_kwargs.__str__, mock_method.name,
+ args.__str__, kwargs.__str__))
+
+ if mock_method.should_raise():
+ mock_method.do_raise()
else:
- self.__calls.append((method_name, args, kwargs))
+ self.__calls.append((mock_method.name, args, kwargs))
+
+ return mock_method
View
BIN mocks.pyc
Binary file not shown.
View
145 tests.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import unittest
-import mockacino
+import mockaccino
from nose.tools import raises
@@ -17,15 +17,15 @@ def method_with_parameter(self, parameter): pass
def method_with_two_parameters(self, a, b): pass
- @raises(mockacino.UnexpectedCallError)
+ @raises(mockaccino.UnexpectedCallError)
def test_unexpected_call_raises_error_on_replay_mode(self):
'''
An UnexpectedCallError should be raised if the mock is on replay
mode and an unexpected method is invoked
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
# this is unexpected and should raise an error
mock.method_that_returns_an_int()
@@ -34,7 +34,7 @@ def test_unexpected_call_never_happens_on_record_mode(self):
'''
Calls on record mode never raise errors
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# This is okay, the method is on record mode
mock.method_that_returns_an_int()
@@ -43,43 +43,43 @@ def test_expected_call_raises_no_error(self):
'''
A call to an expected method should not raise an error
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_that_returns_an_int()
- mockacino.replay(mock)
+ mockaccino.replay(mock)
mock.method_that_returns_an_int()
- @raises(mockacino.UnexpectedCallError)
+ @raises(mockaccino.UnexpectedCallError)
def test_mismatched_method_name_raises_unexpected_call_error(self):
'''
A call to a method different to what was recorded should raise
an unexpected call error
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_that_returns_an_int()
- mockacino.replay(mock)
+ mockaccino.replay(mock)
# Invoking a different method should raise an error
mock.method_with_no_return_value()
- @raises(mockacino.UnexpectedCallError)
+ @raises(mockaccino.UnexpectedCallError)
def test_mismatched_parameter_raises_unexpected_call_error(self):
'''
A call to a method with diferent arguments than what was recorded
should raise an unexpected call error
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_with_parameter(1)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
# Invoking the same method with a different paramenter should raise
mock.method_with_parameter(2)
@@ -89,27 +89,27 @@ def test_correct_parameter_raises_no_error(self):
A call to a method with the same arguments as was recorded should raise
no errors
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_with_parameter(1)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
mock.method_with_parameter(1)
- @raises(mockacino.UnexpectedCallError)
+ @raises(mockaccino.UnexpectedCallError)
def test_mismatched_kwargs_raises_unexpected_call_error(self):
'''
A call to a method with diferent kwargs than what was recorded
should raise an unexpected call error
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_with_parameter(parameter=1)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
# Invoking the same method with a different paramenter should raise
mock.method_with_parameter(parameter=2)
@@ -119,27 +119,126 @@ def test_correct_kwargs_raises_no_error(self):
A call to a method with the same kwargs was recorded should raise
no errors
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_with_parameter(parameter=1)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
mock.method_with_parameter(parameter=1)
- @raises(mockacino.UnexpectedCallError)
+ @raises(mockaccino.UnexpectedCallError)
def test_wrong_argument_order_raises_unexpected_call_error(self):
'''
An unexpected call error should be raised if a method mock is invoked
with a wrong argument order
'''
- mock = mockacino.createMock(self.MockedClass)
+ mock = mockaccino.createMock(self.MockedClass)
# Recorded a method with no return value
mock.method_with_two_parameters(0, 1)
- mockacino.replay(mock)
+ mockaccino.replay(mock)
# Invoking the same method with a different order should raise
mock.method_with_two_parameters(1, 0)
+
+ @raises(mockaccino.UnexpectedCallError)
+ def test_repeating_previously_expected_call_raises_error(self):
+ '''
+ An equal, with same parameters, unexpected call after an
+ expected call raises an error
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ # Recorded a method with no return value
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(1)
+
+ mockaccino.replay(mock)
+
+ # Second invocation the same call, but it's unexpected
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(0)
+
+ @raises(mockaccino.UnexpectedCallError)
+ def test_same_call_wrong_params_after_expected_call_raises_error(self):
+ '''
+ Am equal, but with differente parameters, unexpected call after an
+ expected call raises an error
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ # Recorded a method with no return value
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(1)
+
+ mockaccino.replay(mock)
+
+ # Second invocation the same call, but it's unexpected
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(2)
+
+ def test_times_mock_method_modifier(self):
+ '''
+ The times() mock method modifier should allow multiple calls
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ # Recorded a method with no return value
+ mock.method_with_parameter(0).times(2)
+
+ mockaccino.replay(mock)
+
+ # Second invocation is legal, as mock is recorded with times modifier
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(0)
+
+ @raises(mockaccino.UnexpectedCallError)
+ def test_calling_more_times_than_specified_raises_error(self):
+ '''
+ If the method is called more times than specified, an error
+ should be raised
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ # Recorded a method with no return value
+ mock.method_with_parameter(0).times(2)
+
+ mockaccino.replay(mock)
+
+ # Second invocation is legal, as mock is recorded with times modifier
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(0)
+
+ def test_call_after_times_modifier(self):
+ '''
+ After a method is called enough times, the next expectiation should be set
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ # Recorded a method with no return value
+ mock.method_with_parameter(0).times(2)
+ mock.method_with_two_parameters(0, 1)
+
+ mockaccino.replay(mock)
+
+ # Second invocation is legal, as mock is recorded with times modifier
+ mock.method_with_parameter(0)
+ mock.method_with_parameter(0)
+ mock.method_with_two_parameters(0, 1)
+
+ @raises(ValueError)
+ def test_will_raise(self):
+ '''
+ Mock methods configured to raise errors should do so when invoked
+ '''
+ mock = mockaccino.createMock(self.MockedClass)
+
+ mock.method_that_returns_an_int().will_raise(ValueError)
+
+ mockaccino.replay(mock)
+
+ mock.method_that_returns_an_int()
View
BIN tests.pyc
Binary file not shown.

0 comments on commit 84d4e29

Please sign in to comment.