def setUp(self): """Sets up shared test data files and shared instances""" # Test data directory self.test_dir = "{}/test_data/".format(os.path.dirname(__file__)) # Vulnerability curves self.good_curve = "{}/good_curve.tsv".format(self.test_dir) self.good_curve_2 = "{}/good_curve_2.tsv".format(self.test_dir) self.bad_curve_1 = "{}/bad_curve_1.tsv".format(self.test_dir) self.bad_curve_2 = "{}/bad_curve_2.tsv".format(self.test_dir) # Depth files self.good_depths = "{}/good_depths.tsv".format(self.test_dir) self.bad_depths_1 = "{}/bad_depths_1.tsv".format(self.test_dir) self.bad_depths_2 = "{}/bad_depths_2.tsv".format(self.test_dir) # Test instances self.vulnerability_curve = VulnerabilityCurve(self.good_curve) self.vulnerability_curve_2 = VulnerabilityCurve( self.good_curve_2, max_depth=12, bin_width=2) self.postcode = PostCode("AA1 1AA", "Foo", 10) self.flood_event = FloodEvent(self.good_depths, self.postcode) self.risk = Risk(self.postcode) self.bad_flood_event_1 = FloodEvent(self.bad_depths_1, self.postcode) self.bad_flood_event_2 = FloodEvent(self.bad_depths_2, self.postcode)
def test_risk(self): """Tests the Risk class Tests are as follows: 1. Tests the constructor with good and bad calls 2. Tests that the expected values for floods are correctly calculated 3. Tests that incorrect depth files raise suitable exceptions """ # # 1. Tests the constructor with good and bad calls with self.assertRaises( TypeError, msg="Non-postcode instance should raise TypeError"): Risk(None) # # 2. Tests that the expected values for floods are correctly calculated self.assertAlmostEqual( self.risk.calculate_expected_damage( self.flood_event, self.vulnerability_curve), 10, msg="Expected damage for this calculation should be 10") # # 3. Tests that incorrect depth files raise suitable exceptions # Test bad curve value with self.assertRaises( TypeError, msg="Non-vulnerability curve argument should raise TypeError"): self.risk.calculate_expected_damage(self.flood_event, "bar") # Test bad flood event value with self.assertRaises( TypeError, msg="Non-vulnerability curve argument should raise TypeError"): self.risk.calculate_expected_damage( "foo", self.vulnerability_curve) # Test non-matching flood event and risk postcodes with self.assertRaises( ValueError, msg="Postcode of Risk does not match postcode of flood event"): postcode2 = PostCode("AA2 2AA", "Bar", 10) risk2 = Risk(postcode2) risk2.calculate_expected_damage( self.flood_event, self.vulnerability_curve) # Test incorrect header in depths file with self.assertRaises( IOError, msg="Incorrect header for depths file should raise IOError"): self.risk.calculate_expected_damage( self.bad_flood_event_1, self.vulnerability_curve) # Test out of range value in depths file with self.assertRaises( ValueError, msg="Out-of-range depth values should raise ValueError"): self.risk.calculate_expected_damage( self.bad_flood_event_2, self.vulnerability_curve)
class TestFloodDamage(unittest.TestCase): """Unit tests for the Flood Damage package""" def setUp(self): """Sets up shared test data files and shared instances""" # Test data directory self.test_dir = "{}/test_data/".format(os.path.dirname(__file__)) # Vulnerability curves self.good_curve = "{}/good_curve.tsv".format(self.test_dir) self.good_curve_2 = "{}/good_curve_2.tsv".format(self.test_dir) self.bad_curve_1 = "{}/bad_curve_1.tsv".format(self.test_dir) self.bad_curve_2 = "{}/bad_curve_2.tsv".format(self.test_dir) # Depth files self.good_depths = "{}/good_depths.tsv".format(self.test_dir) self.bad_depths_1 = "{}/bad_depths_1.tsv".format(self.test_dir) self.bad_depths_2 = "{}/bad_depths_2.tsv".format(self.test_dir) # Test instances self.vulnerability_curve = VulnerabilityCurve(self.good_curve) self.vulnerability_curve_2 = VulnerabilityCurve( self.good_curve_2, max_depth=12, bin_width=2) self.postcode = PostCode("AA1 1AA", "Foo", 10) self.flood_event = FloodEvent(self.good_depths, self.postcode) self.risk = Risk(self.postcode) self.bad_flood_event_1 = FloodEvent(self.bad_depths_1, self.postcode) self.bad_flood_event_2 = FloodEvent(self.bad_depths_2, self.postcode) def test_postcode(self): """Tests the PostCode class :return: """ # # Test constructor with good and bad values self.assertIsInstance( self.flood_event, FloodEvent, msg="Expected FloodEvent object") with self.assertRaises( ValueError, msg="Invalid postcode should raise value error"): self.postcode = PostCode("Foo", "Bar", 10) with self.assertRaises( ValueError, msg="Invalid postcode should raise value error"): self.postcode = PostCode(1, "Bar", 10) with self.assertRaises( TypeError, msg="Non-string town should raise type error"): self.postcode = PostCode("AA1 1AA", None, 10) with self.assertRaises( TypeError, msg="Non-int town should raise type error"): self.postcode = PostCode("AA1 1AA", "Foo", "Bar") def test_curve(self): """Tests the Vulnerability Curve classes and methods Tests are as follows: 1. Tests that damage_from_depth function returns correct output 2. Test exceptions raised by damage_from_depth function 3. Tests suitable exceptions are raised with an invalid curve file """ # # 1. Test Damage outputs from a valid curve self.assertEqual( self.vulnerability_curve.damage_from_depth(0), 0, "Depth zero should return zero damage") self.assertEqual( self.vulnerability_curve.damage_from_depth(0.1), 10, "Depth 0.1 should return damage = 10 with the 'good' test curve") self.assertEqual( self.vulnerability_curve.damage_from_depth(4), 40, "Depth 4 should return damage = 40 with the 'good' test curve") # Test non-standard vulnerability curve (max_depth=12, bin_width=2) self.assertEqual( self.vulnerability_curve_2.damage_from_depth(11.0), 120, "Depth 11 should return damage = 120 with the second test curve") # # 2. Test suitable exceptions are raise with out-of-range depth values with self.assertRaises( ValueError, msg="Out-of-range depth should raise ValueError"): self.vulnerability_curve.damage_from_depth(11) with self.assertRaises( ValueError, msg="Out-of-range depth should raise ValueError"): self.vulnerability_curve.damage_from_depth(-1.1) with self.assertRaises( TypeError, msg="Non-numeric value should raise TypeError"): self.vulnerability_curve.damage_from_depth("foo") with self.assertRaises( ValueError, msg="Out-of-range depth should raise ValueError"): self.vulnerability_curve.damage_from_depth(11) # # 3). Tests suitable exceptions are raised with an invalid curve file # Check test file 'bad_curve_1.tsv' exists self.assertTrue( os.path.isfile(self.bad_curve_1), "Missing bad_curve_1.tsv test file") # Check exception is raised if the expected file header is not present with self.assertRaises( IOError, msg="Incorrect curve file should raise IOError"): VulnerabilityCurve(self.bad_curve_1) # Check test file 'bad_curve_2.tsv' exists self.assertTrue( os.path.isfile(self.bad_curve_2), "Missing bad_curve_2.tsv test file") # Check exception is raised if file is incorrectly formatted with self.assertRaises( IOError, msg="Incorrect curve file should raise IOError"): VulnerabilityCurve(self.bad_curve_2) def test_flood_event(self): """Tests the FloodEvent classes and methods 1. Tests the constructor with good and bad calls """ # # 1. Test Constructor # Test valid call self.assertEqual( type(self.flood_event), FloodEvent, msg="Expected FloodEvent instance") with self.assertRaises( TypeError, msg="Incorrect postcode should raise type error"): FloodEvent(self.good_depths, None) with self.assertRaises( TypeError, msg="Incorrect depth file input should raise type error"): FloodEvent(None, self.postcode) with self.assertRaises( IOError, msg="Non-existant depth file should raise IO error"): FloodEvent("foo", self.postcode) def test_risk(self): """Tests the Risk class Tests are as follows: 1. Tests the constructor with good and bad calls 2. Tests that the expected values for floods are correctly calculated 3. Tests that incorrect depth files raise suitable exceptions """ # # 1. Tests the constructor with good and bad calls with self.assertRaises( TypeError, msg="Non-postcode instance should raise TypeError"): Risk(None) # # 2. Tests that the expected values for floods are correctly calculated self.assertAlmostEqual( self.risk.calculate_expected_damage( self.flood_event, self.vulnerability_curve), 10, msg="Expected damage for this calculation should be 10") # # 3. Tests that incorrect depth files raise suitable exceptions # Test bad curve value with self.assertRaises( TypeError, msg="Non-vulnerability curve argument should raise TypeError"): self.risk.calculate_expected_damage(self.flood_event, "bar") # Test bad flood event value with self.assertRaises( TypeError, msg="Non-vulnerability curve argument should raise TypeError"): self.risk.calculate_expected_damage( "foo", self.vulnerability_curve) # Test non-matching flood event and risk postcodes with self.assertRaises( ValueError, msg="Postcode of Risk does not match postcode of flood event"): postcode2 = PostCode("AA2 2AA", "Bar", 10) risk2 = Risk(postcode2) risk2.calculate_expected_damage( self.flood_event, self.vulnerability_curve) # Test incorrect header in depths file with self.assertRaises( IOError, msg="Incorrect header for depths file should raise IOError"): self.risk.calculate_expected_damage( self.bad_flood_event_1, self.vulnerability_curve) # Test out of range value in depths file with self.assertRaises( ValueError, msg="Out-of-range depth values should raise ValueError"): self.risk.calculate_expected_damage( self.bad_flood_event_2, self.vulnerability_curve)