Skip to content

Commit

Permalink
Implement checks for messages being raised with exceptions
Browse files Browse the repository at this point in the history
(Fixes exercism#1080)

* Add self.assertRaisesWithMessage method to relevant exercise tests
    - Uses self.assertRaisesRegex
    - Checks only for the presence of a message, not content
* Add meaningful messages to failing examples
* octal: Switch to using a context manager for exception tests
  • Loading branch information
Nathan Parsons committed Nov 21, 2017
1 parent 0965483 commit d15bca4
Show file tree
Hide file tree
Showing 42 changed files with 555 additions and 146 deletions.
28 changes: 19 additions & 9 deletions exercises/all-your-base/all_your_base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,41 +44,51 @@ def test_leading_zeros(self):
self.assertEqual(rebase(7, [0, 6, 0], 10), [4, 2])

def test_first_base_is_one(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(1, [], 10)

def test_first_base_is_zero(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(0, [], 10)

def test_first_base_is_negative(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(-2, [1], 10)

def test_negative_digit(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1, -1, 1, 0, 1, 0], 10)

def test_invalid_positive_digit(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1, 2, 1, 0, 1, 0], 10)

def test_second_base_is_one(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1, 0, 1, 0, 1, 0], 1)

def test_second_base_is_zero(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(10, [7], 0)

def test_second_base_is_negative(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(2, [1], -7)

def test_both_bases_are_negative(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
rebase(-2, [1], -7)

# Utility functions
def setUp(self):
try:
self.assertRaisesRegex = self.assertRaisesRegexp
except AttributeError:
pass

def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == '__main__':
unittest.main()
18 changes: 14 additions & 4 deletions exercises/binary-search/binary_search_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,31 @@ def test_finds_value_in_array_of_even_length(self):
5)

def test_identifies_value_missing(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
binary_search([1, 3, 4, 6, 8, 9, 11], 7)

def test_value_smaller_than_arrays_minimum(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
binary_search([1, 3, 4, 6, 8, 9, 11], 0)

def test_value_larger_than_arrays_maximum(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
binary_search([1, 3, 4, 6, 8, 9, 11], 13)

def test_empty_array(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
binary_search([], 1)

# Utility functions
def setUp(self):
try:
self.assertRaisesRegex = self.assertRaisesRegexp
except AttributeError:
pass

def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == '__main__':
unittest.main()
18 changes: 14 additions & 4 deletions exercises/binary/binary_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,31 @@ def test_binary_10001101000_is_decimal_1128(self):
self.assertEqual(parse_binary("10001101000"), 1128)

def test_invalid_binary_text_only(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
parse_binary("carrot")

def test_invalid_binary_number_not_base2(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
parse_binary("102011")

def test_invalid_binary_numbers_with_text(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
parse_binary("10nope")

def test_invalid_binary_text_with_numbers(self):
with self.assertRaises(ValueError):
with self.assertRaisesWithMessage(ValueError):
parse_binary("nope10")

# Utility functions
def setUp(self):
try:
self.assertRaisesRegex = self.assertRaisesRegexp
except AttributeError:
pass

def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == '__main__':
unittest.main()
20 changes: 15 additions & 5 deletions exercises/circular-buffer/circular_buffer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class CircularBufferTest(unittest.TestCase):
def test_read_empty_buffer(self):
buf = CircularBuffer(1)
with self.assertRaises(BufferEmptyException):
with self.assertRaisesWithMessage(BufferEmptyException):
buf.read()

def test_read_just_written_item(self):
Expand All @@ -24,7 +24,7 @@ def test_write_and_read_back_one_item(self):
buf = CircularBuffer(1)
buf.write('1')
self.assertEqual(buf.read(), '1')
with self.assertRaises(BufferEmptyException):
with self.assertRaisesWithMessage(BufferEmptyException):
buf.read()

def test_write_and_read_back_multiple_items_ordered(self):
Expand All @@ -37,7 +37,7 @@ def test_write_and_read_back_multiple_items_ordered(self):
def test_full_buffer_cant_written(self):
buf = CircularBuffer(1)
buf.write('1')
with self.assertRaises(BufferFullException):
with self.assertRaisesWithMessage(BufferFullException):
buf.write('2')

def test_alternate_write_and_read(self):
Expand All @@ -60,7 +60,7 @@ def test_clearing_buffer(self):
buf = CircularBuffer(1)
buf.write('1')
buf.clear()
with self.assertRaises(BufferEmptyException):
with self.assertRaisesWithMessage(BufferEmptyException):
buf.read()

def test_clear_free_buffer_for_write(self):
Expand Down Expand Up @@ -95,7 +95,7 @@ def test_write_full_buffer(self):
buf = CircularBuffer(2)
buf.write('1')
buf.write('2')
with self.assertRaises(BufferFullException):
with self.assertRaisesWithMessage(BufferFullException):
buf.write('A')

def test_over_write_replaces_oldest_remaning_item(self):
Expand All @@ -110,6 +110,16 @@ def test_over_write_replaces_oldest_remaning_item(self):
self.assertEqual(buf.read(), '4')
self.assertEqual(buf.read(), '5')

# Utility functions
def setUp(self):
try:
self.assertRaisesRegex = self.assertRaisesRegexp
except AttributeError:
pass

def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == '__main__':
unittest.main()
4 changes: 2 additions & 2 deletions exercises/circular-buffer/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def clear(self):

def write(self, data):
if all(self.buffer):
raise BufferFullException
raise BufferFullException("Circular buffer is full")
self._update_buffer(data)
self.write_point = (self.write_point + 1) % len(self.buffer)

Expand All @@ -37,7 +37,7 @@ def overwrite(self, data):

def read(self):
if not any(self.buffer):
raise BufferEmptyException
raise BufferEmptyException("Circular buffer is empty")
data = chr(self.buffer[self.read_point])
self.buffer[self.read_point] = 0
self.read_point = (self.read_point + 1) % len(self.buffer)
Expand Down
16 changes: 13 additions & 3 deletions exercises/error-handling/error_handling_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def __exit__(self, *args):
def do_something(self):
self.did_something = True
if self.fail_something:
raise Exception()
raise Exception("Failed while doing something")


class ErrorHandlingTest(unittest.TestCase):
def test_throw_exception(self):
with self.assertRaises(Exception):
with self.assertRaisesWithMessage(Exception):
er.handle_error_by_throwing_exception()

def test_return_none(self):
Expand All @@ -54,7 +54,7 @@ def test_return_tuple(self):

def test_filelike_objects_are_closed_on_exception(self):
filelike_object = FileLike(fail_something=True)
with self.assertRaises(Exception):
with self.assertRaisesWithMessage(Exception):
er.filelike_objects_are_closed_on_exception(filelike_object)
self.assertIs(filelike_object.is_open, False,
'filelike_object should be closed')
Expand All @@ -73,6 +73,16 @@ def test_filelike_objects_are_closed_without_exception(self):
self.assertIs(filelike_object.did_something, True,
'filelike_object should call do_something()')

# Utility functions
def setUp(self):
try:
self.assertRaisesRegex = self.assertRaisesRegexp
except AttributeError:
pass

def assertRaisesWithMessage(self, exception):
return self.assertRaisesRegex(exception, r".+")


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion exercises/error-handling/example.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def handle_error_by_throwing_exception():
raise Exception()
raise Exception("Meaningful message describing the source of the error")


def handle_error_by_returning_none(input_data):
Expand Down
16 changes: 7 additions & 9 deletions exercises/forth/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def evaluate(input_data):
values.pop(0)
key = values.pop(0)
if is_integer(key):
raise ValueError()
raise ValueError("Integers cannot be redefined")
defines[key] = values
stack = []
input_data = input_data[-1].split()
Expand All @@ -38,10 +38,10 @@ def evaluate(input_data):
elif word == '*':
stack.append(stack.pop() * stack.pop())
elif word == '/':
divider = stack.pop()
if divider == 0:
raise ZeroDivisionError()
stack.append(int(stack.pop() / divider))
divisor = stack.pop()
if divisor == 0:
raise ZeroDivisionError("Attempted to divide by zero")
stack.append(int(stack.pop() / divisor))
elif word == 'dup':
stack.append(stack[-1])
elif word == 'drop':
Expand All @@ -52,9 +52,7 @@ def evaluate(input_data):
elif word == 'over':
stack.append(stack[-2])
else:
raise ValueError()
except ZeroDivisionError:
raise
raise ValueError("{} has not been defined".format(word))
except IndexError:
raise StackUnderflowError()
raise StackUnderflowError("Insufficient number of items in stack")
return stack
Loading

0 comments on commit d15bca4

Please sign in to comment.