16

I'm trying to write a unit test that verifies a KeyError is created when a bad key is passed to a dictionary.

The code that raises the exception:

connections = SettingsManager().get_connections()
try:
    connection = connections[self.conn_name]
except Exception:
    self.log.error("Connection %s does not exist, exiting." % conn_name)
    self.log.error(sys.exc_info()[0])
    raise

I've looked and found KeyError tests using lambda, but I've not had much luck. Here is the test I have thus far, but it errors with the actual KeyError.

def test_bad_connection(self):
    #Testing to see if a non existant connection will fail gracefully.
    records = [1, 2]
    given_result = DataConnector("BadConnection").generate_data(self.table, records)
    expected_result = "BadConnection"

    self.assertRaises(KeyError, given_result[:1])
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
OpenDataAlex
  • 1,375
  • 5
  • 19
  • 39

2 Answers2

15

assertRaises() will call the function for you, and assert that that call raises the exception:

records = [1, 2]
connector = DataConnector("BadConnection")

self.assertRaises(KeyError, connector.generate_data, self.table, records)

Alternatively, use assertRaises() as a context manager:

with self.assertRaises(KeyError) as raises:
    DataConnector("BadConnection").generate_data(self.table, records)

which has the added advantage that the context manager then lets you access the raised exception:

self.assertEqual(raises.exception.message, "BadConnection")
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thank you - that explains it perfectly! For some reason, I was unable to use the 'raises.exception.msg', rather I used 'raises.exception[0]' and that gave me the expected result. – OpenDataAlex Jun 20 '14 at 15:35
  • 1
    @OpenDataAlex: sorry; the `exception.args[0]` value is also exposed as `exception.message`; I just misspelled it. – Martijn Pieters Jun 20 '14 at 15:37
14

self.assertRaise() only takes a callable, so while

self.assertRaises(KeyError, given_result[:1])
would give you an actual KeyError while testing,

self.assertRaises(KeyError, lambda: given_result[:1])
should work.

In general:
Does not work: self.assertRaises(KeyError, mydict[mykey]) #KeyError in tests
Does work: self.assertRaises(KeyError, lambda: mydict[mykey])
Does work: self.assertRaises(KeyError, mydict.__getitem__, mykey) #but is requires an actual dict, instead of a function

Roman
  • 8,826
  • 10
  • 63
  • 103
  • 2
    This answer was easier to understand and reason with than the accepted answer, because it provides a more general solution and not a custom solution. Thanks! – Vadorequest Nov 25 '18 at 12:50
  • 1
    `self.assertRaises(KeyError, mydict.get(mykey))` also works. – Jonatan Feb 02 '19 at 10:54