From bc3b94f264ca3e126dd963ef9108246fd78dddd5 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:19:52 +0530 Subject: [PATCH 01/21] nose2 - function style test case --- stock_alerter/tests/test_stock.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index a7453d4..fc101cd 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -5,6 +5,11 @@ from ..stock import Stock, StockSignal +def test_price_of_a_new_stock_class_should_be_None(): + goog = Stock("GOOG") + assert goog.price is None + + class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 3fb38fd2ef457cfb01853750b258050db764225f Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:21:14 +0530 Subject: [PATCH 02/21] nose2 - setup and teardown --- stock_alerter/tests/test_stock.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index fc101cd..9d96ee9 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -5,9 +5,20 @@ from ..stock import Stock, StockSignal -def test_price_of_a_new_stock_class_should_be_None(): +def setup_test(): + global goog goog = Stock("GOOG") + + +def teardown_test(): + global goog + goog = None + + +def test_price_of_a_new_stock_class_should_be_None(): assert goog.price is None +test_price_of_a_new_stock_class_should_be_None.setup = setup_test +test_price_of_a_new_stock_class_should_be_None.teardown = teardown_test class StockTest(unittest.TestCase): From ac686a1a23e334f3a092cf20be26be7d124cc530 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:22:25 +0530 Subject: [PATCH 03/21] nose2 - parametrised tests --- stock_alerter/tests/test_stock.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index 9d96ee9..75f0937 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -2,6 +2,8 @@ import collections from datetime import datetime, timedelta +from nose2.tools.params import params + from ..stock import Stock, StockSignal @@ -21,6 +23,24 @@ def test_price_of_a_new_stock_class_should_be_None(): test_price_of_a_new_stock_class_should_be_None.teardown = teardown_test +def given_a_series_of_prices(stock, prices): + timestamps = [datetime(2014, 2, 10), datetime(2014, 2, 11), + datetime(2014, 2, 12), datetime(2014, 2, 13)] + for timestamp, price in zip(timestamps, prices): + stock.update(timestamp, price) + + +@params( + ([8, 10, 12], True), + ([8, 12, 10], False), + ([8, 10, 10], False) +) +def test_stock_trends(prices, expected_output): + goog = Stock("GOOG") + given_a_series_of_prices(goog, prices) + assert goog.is_increasing_trend() == expected_output + + class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 6c71a99f449e2800770062786f754c8decfc8670 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:23:42 +0530 Subject: [PATCH 04/21] nose2 - generated tests --- stock_alerter/tests/test_stock.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index 75f0937..27fd3c0 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -41,6 +41,17 @@ def test_stock_trends(prices, expected_output): assert goog.is_increasing_trend() == expected_output +def test_trend_with_all_consecutive_values_upto_100(): + for i in range(100): + yield stock_trends_with_consecutive_prices, [i, i+1, i+2] + + +def stock_trends_with_consecutive_prices(prices): + goog = Stock("GOOG") + given_a_series_of_prices(goog, prices) + assert goog.is_increasing_trend() + + class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 7b6daf6be23102550a0674dd99ce360ec9eac0f4 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:30:14 +0530 Subject: [PATCH 05/21] nose2 - Using Such DSL with the Layers plugin --- nose2.cfg | 9 +++++++++ requirements.txt | 1 + stock_alerter/tests/test_stock.py | 32 +++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 nose2.cfg create mode 100644 requirements.txt diff --git a/nose2.cfg b/nose2.cfg new file mode 100644 index 0000000..c902aa5 --- /dev/null +++ b/nose2.cfg @@ -0,0 +1,9 @@ +[unittest] +test-file-pattern=test_*.py +test-method-prefix=test +plugins = nose2.plugins.layers +exclude-plugins = nose2.plugins.doctest + +[layer-reporter] +always-on = False +colors = False diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1f671a5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +nose2 \ No newline at end of file diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index 27fd3c0..b97de2b 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from nose2.tools.params import params +from nose2.tools import such from ..stock import Stock, StockSignal @@ -52,6 +53,37 @@ def stock_trends_with_consecutive_prices(prices): assert goog.is_increasing_trend() +with such.A("Stock class") as it: + + @it.has_setup + def setup(): + it.goog = Stock("GOOG") + + with it.having("a price method"): + @it.has_setup + def setup(): + it.goog.update(datetime(2014, 2, 12), price=10) + + @it.should("return the price") + def test(case): + assert it.goog.price == 10 + + @it.should("return the latest price") + def test(case): + it.goog.update(datetime(2014, 2, 11), price=15) + assert it.goog.price == 10 + + with it.having("a trend method"): + @it.should("return True if the last three updates were increasing") + def test(case): + it.goog.update(datetime(2014, 2, 11), price=12) + it.goog.update(datetime(2014, 2, 12), price=13) + it.goog.update(datetime(2014, 2, 13), price=14) + assert it.goog.is_increasing_trend() + + it.createTests(globals()) + + class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 45e5705bb7d1e8a441395fc1c6f6376a6b09c5d2 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:39:33 +0530 Subject: [PATCH 06/21] nose2 - using a config file --- nose2.cfg | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nose2.cfg b/nose2.cfg index c902aa5..9988d74 100644 --- a/nose2.cfg +++ b/nose2.cfg @@ -1,9 +1,19 @@ [unittest] test-file-pattern=test_*.py test-method-prefix=test -plugins = nose2.plugins.layers +plugins = nose2.plugins.coverage + nose2.plugins.junitxml + nose2.plugins.layers exclude-plugins = nose2.plugins.doctest [layer-reporter] -always-on = False +always-on = True colors = False + +[junit-xml] +always-on = False +path = nose2.xml + +[coverage] +always-on = False +coverage-report = ["html", "xml"] From 27992822c7d1b09f69f9efc9582c473dd6ca35ec Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Wed, 25 Mar 2015 09:44:26 +0530 Subject: [PATCH 07/21] Removed nose2 specific tests --- stock_alerter/tests/test_stock.py | 79 ------------------------------- 1 file changed, 79 deletions(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index b97de2b..a7453d4 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -2,88 +2,9 @@ import collections from datetime import datetime, timedelta -from nose2.tools.params import params -from nose2.tools import such - from ..stock import Stock, StockSignal -def setup_test(): - global goog - goog = Stock("GOOG") - - -def teardown_test(): - global goog - goog = None - - -def test_price_of_a_new_stock_class_should_be_None(): - assert goog.price is None -test_price_of_a_new_stock_class_should_be_None.setup = setup_test -test_price_of_a_new_stock_class_should_be_None.teardown = teardown_test - - -def given_a_series_of_prices(stock, prices): - timestamps = [datetime(2014, 2, 10), datetime(2014, 2, 11), - datetime(2014, 2, 12), datetime(2014, 2, 13)] - for timestamp, price in zip(timestamps, prices): - stock.update(timestamp, price) - - -@params( - ([8, 10, 12], True), - ([8, 12, 10], False), - ([8, 10, 10], False) -) -def test_stock_trends(prices, expected_output): - goog = Stock("GOOG") - given_a_series_of_prices(goog, prices) - assert goog.is_increasing_trend() == expected_output - - -def test_trend_with_all_consecutive_values_upto_100(): - for i in range(100): - yield stock_trends_with_consecutive_prices, [i, i+1, i+2] - - -def stock_trends_with_consecutive_prices(prices): - goog = Stock("GOOG") - given_a_series_of_prices(goog, prices) - assert goog.is_increasing_trend() - - -with such.A("Stock class") as it: - - @it.has_setup - def setup(): - it.goog = Stock("GOOG") - - with it.having("a price method"): - @it.has_setup - def setup(): - it.goog.update(datetime(2014, 2, 12), price=10) - - @it.should("return the price") - def test(case): - assert it.goog.price == 10 - - @it.should("return the latest price") - def test(case): - it.goog.update(datetime(2014, 2, 11), price=15) - assert it.goog.price == 10 - - with it.having("a trend method"): - @it.should("return True if the last three updates were increasing") - def test(case): - it.goog.update(datetime(2014, 2, 11), price=12) - it.goog.update(datetime(2014, 2, 12), price=13) - it.goog.update(datetime(2014, 2, 13), price=14) - assert it.goog.is_increasing_trend() - - it.createTests(globals()) - - class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 386e11bd76cd1d091190e9a6769f7a7f14a4ff4f Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:03:00 +0530 Subject: [PATCH 08/21] Creating a basic test suite --- run_test.py | 7 +++++++ stock_alerter/tests/test_stock.py | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 run_test.py diff --git a/run_test.py b/run_test.py new file mode 100644 index 0000000..6e6679c --- /dev/null +++ b/run_test.py @@ -0,0 +1,7 @@ +import unittest + +from stock_alerter.tests import test_stock + +if __name__ == "__main__": + runner = unittest.TextTestRunner() + runner.run(test_stock.suite()) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index a7453d4..efd3e0c 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -5,6 +5,12 @@ from ..stock import Stock, StockSignal +def suite(): + test_suite = unittest.TestSuite() + test_suite.addTest(StockTest("test_stock_update")) + return test_suite + + class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") From 3cdd2c64f010d1cd1e3fcd0aef4e5777651a7757 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:04:53 +0530 Subject: [PATCH 09/21] Using a test loader --- stock_alerter/tests/test_stock.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index efd3e0c..acda350 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -6,8 +6,10 @@ def suite(): + loader = unittest.TestLoader() test_suite = unittest.TestSuite() test_suite.addTest(StockTest("test_stock_update")) + test_suite.addTest(loader.loadTestsFromTestCase(StockCrossOverSignalTest)) return test_suite From 8f953ab29782da78448b7a00c718877f12352cc8 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:06:44 +0530 Subject: [PATCH 10/21] Using the load_tests protocol --- stock_alerter/tests/test_stock.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index acda350..f9e0bf7 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -1,3 +1,4 @@ +import sys import unittest import collections from datetime import datetime, timedelta @@ -5,12 +6,12 @@ from ..stock import Stock, StockSignal -def suite(): - loader = unittest.TestLoader() - test_suite = unittest.TestSuite() - test_suite.addTest(StockTest("test_stock_update")) - test_suite.addTest(loader.loadTestsFromTestCase(StockCrossOverSignalTest)) - return test_suite +def load_tests(loader, tests, pattern): + suite = unittest.TestSuite() + suite.addTest(loader.loadTestsFromTestCase(StockTest)) + if not sys.platform.startswith("win"): + suite.addTest(loader.loadTestsFromTestCase(StockCrossOverSignalTest)) + return suite class StockTest(unittest.TestCase): From a5b2011a8a7d5e481a0c39dd994ce7997da44c0c Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:09:12 +0530 Subject: [PATCH 11/21] Skipping tests --- stock_alerter/tests/test_stock.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index f9e0bf7..a90d199 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -1,4 +1,3 @@ -import sys import unittest import collections from datetime import datetime, timedelta @@ -6,14 +5,6 @@ from ..stock import Stock, StockSignal -def load_tests(loader, tests, pattern): - suite = unittest.TestSuite() - suite.addTest(loader.loadTestsFromTestCase(StockTest)) - if not sys.platform.startswith("win"): - suite.addTest(loader.loadTestsFromTestCase(StockCrossOverSignalTest)) - return suite - - class StockTest(unittest.TestCase): def setUp(self): self.goog = Stock("GOOG") @@ -21,6 +12,7 @@ def setUp(self): def test_price_of_a_new_stock_class_should_be_None(self): self.assertIsNone(self.goog.price) + @unittest.skip("skip this test for now") def test_stock_update(self): """An update should set the price on the stock object From 5da0924ddeee784ae5445e6939dc361150c02747 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:11:19 +0530 Subject: [PATCH 12/21] Conditionally skipping tests --- stock_alerter/tests/test_stock.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index a90d199..a69edad 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -1,3 +1,4 @@ +import sys import unittest import collections from datetime import datetime, timedelta @@ -12,7 +13,7 @@ def setUp(self): def test_price_of_a_new_stock_class_should_be_None(self): self.assertIsNone(self.goog.price) - @unittest.skip("skip this test for now") + @unittest.skipIf(sys.platform.startswith("win"), "skip on windows") def test_stock_update(self): """An update should set the price on the stock object @@ -21,6 +22,7 @@ def test_stock_update(self): self.goog.update(datetime(2014, 2, 12), price=10) self.assertEqual(10, self.goog.price) + @unittest.skipUnless(sys.platform.startswith("win"), "only run on windows") def test_negative_price_should_throw_ValueError(self): with self.assertRaises(ValueError): self.goog.update(datetime(2014, 2, 13), -1) From 780d5a13488ead5cf0a5c5fbab290c3e56f5769d Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:15:21 +0530 Subject: [PATCH 13/21] Writing a custom test loader to load tests by attribute --- run_test.py | 21 +++++++++++++++++++-- stock_alerter/tests/test_stock.py | 5 ++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/run_test.py b/run_test.py index 6e6679c..5595d1a 100644 --- a/run_test.py +++ b/run_test.py @@ -1,7 +1,24 @@ import unittest -from stock_alerter.tests import test_stock + +class AttribLoader(unittest.TestLoader): + def __init__(self, attrib): + self.attrib = attrib + + def loadTestsFromModule(self, module, use_load_tests=False): + return super().loadTestsFromModule(module, use_load_tests=False) + + def getTestCaseNames(self, testCaseClass): + test_names = super().getTestCaseNames(testCaseClass) + filtered_test_names = [test + for test in test_names + if hasattr(getattr(testCaseClass, test), + self.attrib)] + return filtered_test_names + if __name__ == "__main__": + loader = AttribLoader("slow") + test_suite = loader.discover(".") runner = unittest.TextTestRunner() - runner.run(test_stock.suite()) + runner.run(test_suite) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index a69edad..ea1ba54 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -1,4 +1,3 @@ -import sys import unittest import collections from datetime import datetime, timedelta @@ -13,7 +12,6 @@ def setUp(self): def test_price_of_a_new_stock_class_should_be_None(self): self.assertIsNone(self.goog.price) - @unittest.skipIf(sys.platform.startswith("win"), "skip on windows") def test_stock_update(self): """An update should set the price on the stock object @@ -22,7 +20,8 @@ def test_stock_update(self): self.goog.update(datetime(2014, 2, 12), price=10) self.assertEqual(10, self.goog.price) - @unittest.skipUnless(sys.platform.startswith("win"), "only run on windows") + test_stock_update.slow = True + def test_negative_price_should_throw_ValueError(self): with self.assertRaises(ValueError): self.goog.update(datetime(2014, 2, 13), -1) From 8115678d47004812eb9e90b667d2c53b773b9064 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:23:25 +0530 Subject: [PATCH 14/21] Expected failure --- stock_alerter/tests/test_stock.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index ea1ba54..c981e61 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -12,15 +12,14 @@ def setUp(self): def test_price_of_a_new_stock_class_should_be_None(self): self.assertIsNone(self.goog.price) + @unittest.expectedFailure def test_stock_update(self): """An update should set the price on the stock object We will be using the `datetime` module for the timestamp """ self.goog.update(datetime(2014, 2, 12), price=10) - self.assertEqual(10, self.goog.price) - - test_stock_update.slow = True + self.assertEqual(100, self.goog.price) def test_negative_price_should_throw_ValueError(self): with self.assertRaises(ValueError): From 600351309edb24feffa0cb1ce69e8fde47cbfd9b Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:38:31 +0530 Subject: [PATCH 15/21] Data driven tests with subTest --- stock_alerter/tests/test_stock.py | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/stock_alerter/tests/test_stock.py b/stock_alerter/tests/test_stock.py index c981e61..cfb644b 100644 --- a/stock_alerter/tests/test_stock.py +++ b/stock_alerter/tests/test_stock.py @@ -12,14 +12,13 @@ def setUp(self): def test_price_of_a_new_stock_class_should_be_None(self): self.assertIsNone(self.goog.price) - @unittest.expectedFailure def test_stock_update(self): """An update should set the price on the stock object We will be using the `datetime` module for the timestamp """ self.goog.update(datetime(2014, 2, 12), price=10) - self.assertEqual(100, self.goog.price) + self.assertEqual(10, self.goog.price) def test_negative_price_should_throw_ValueError(self): with self.assertRaises(ValueError): @@ -37,26 +36,24 @@ def test_price_is_the_latest_even_if_updates_are_made_out_of_order(self): class StockTrendTest(unittest.TestCase): - def setUp(self): - self.goog = Stock("GOOG") - - def given_a_series_of_prices(self, prices): + def given_a_series_of_prices(self, goog, prices): timestamps = [datetime(2014, 2, 10), datetime(2014, 2, 11), datetime(2014, 2, 12), datetime(2014, 2, 13)] for timestamp, price in zip(timestamps, prices): - self.goog.update(timestamp, price) - - def test_increasing_trend_is_true_if_price_increase_for_3_updates(self): - self.given_a_series_of_prices([8, 10, 12]) - self.assertTrue(self.goog.is_increasing_trend()) - - def test_increasing_trend_is_false_if_price_decreases(self): - self.given_a_series_of_prices([8, 12, 10]) - self.assertFalse(self.goog.is_increasing_trend()) - - def test_increasing_trend_is_false_if_price_equal(self): - self.given_a_series_of_prices([8, 10, 10]) - self.assertFalse(self.goog.is_increasing_trend()) + goog.update(timestamp, price) + + def test_stock_trends(self): + dataset = [ + ([8, 10, 12], True), + ([8, 12, 10], False), + ([8, 10, 10], False) + ] + for data in dataset: + prices, output = data + with self.subTest(prices=prices, output=output): + goog = Stock("GOOG") + self.given_a_series_of_prices(goog, prices) + self.assertEqual(output, goog.is_increasing_trend()) class StockCrossOverSignalTest(unittest.TestCase): From 21f8b9977798409ba1d3ade1d97999ed28c08f47 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:49:38 +0530 Subject: [PATCH 16/21] Implementing a spy --- stock_alerter/tests/test_alert.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stock_alerter/tests/test_alert.py b/stock_alerter/tests/test_alert.py index d7f38e1..2be7039 100644 --- a/stock_alerter/tests/test_alert.py +++ b/stock_alerter/tests/test_alert.py @@ -22,3 +22,15 @@ def test_action_is_executed_when_rule_matches(self): alert.connect(exchange) exchange["GOOG"].update(datetime(2014, 2, 10), 11) action.execute.assert_called_with("sample alert") + + def test_action_doesnt_fire_if_rule_doesnt_match(self): + goog = Stock("GOOG") + exchange = {"GOOG": goog} + rule = PriceRule("GOOG", lambda stock: stock.price > 10) + rule_spy = mock.MagicMock(wraps=rule) + action = mock.MagicMock() + alert = Alert("sample alert", rule_spy, action) + alert.connect(exchange) + alert.check_rule(goog) + rule_spy.matches.assert_called_with(exchange) + self.assertFalse(action.execute.called) From 5d10c464c5ef21d291e768c9b44ff9206ba53b2d Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:51:27 +0530 Subject: [PATCH 17/21] Validating a sequence of calls across different objects --- stock_alerter/tests/test_alert.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stock_alerter/tests/test_alert.py b/stock_alerter/tests/test_alert.py index 2be7039..c8b685f 100644 --- a/stock_alerter/tests/test_alert.py +++ b/stock_alerter/tests/test_alert.py @@ -34,3 +34,18 @@ def test_action_doesnt_fire_if_rule_doesnt_match(self): alert.check_rule(goog) rule_spy.matches.assert_called_with(exchange) self.assertFalse(action.execute.called) + + def test_action_fires_when_rule_matches(self): + goog = Stock("GOOG") + exchange = {"GOOG": goog} + main_mock = mock.MagicMock() + rule = main_mock.rule + rule.matches.return_value = True + rule.depends_on.return_value = {"GOOG"} + action = main_mock.action + alert = Alert("sample alert", rule, action) + alert.connect(exchange) + goog.update(datetime(2014, 5, 14), 11) + main_mock.assert_has_calls( + [mock.call.rule.matches(exchange), + mock.call.action.execute("sample alert")]) From cfab237bbd5cf7c83dc426458c5999fe01d0eaad Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 08:53:04 +0530 Subject: [PATCH 18/21] Strictly asserting a sequence of calls --- stock_alerter/tests/test_alert.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stock_alerter/tests/test_alert.py b/stock_alerter/tests/test_alert.py index c8b685f..dc83efe 100644 --- a/stock_alerter/tests/test_alert.py +++ b/stock_alerter/tests/test_alert.py @@ -46,6 +46,7 @@ def test_action_fires_when_rule_matches(self): alert = Alert("sample alert", rule, action) alert.connect(exchange) goog.update(datetime(2014, 5, 14), 11) - main_mock.assert_has_calls( - [mock.call.rule.matches(exchange), - mock.call.action.execute("sample alert")]) + self.assertEqual([mock.call.rule.depends_on(), + mock.call.rule.matches(exchange), + mock.call.action.execute("sample alert")], + main_mock.mock_calls) From 9d466f7bd9563f75485bf1561daf6dd37f622c88 Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Thu, 26 Mar 2015 09:05:45 +0530 Subject: [PATCH 19/21] Mocking the open function --- stock_alerter/tests/test_reader.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 stock_alerter/tests/test_reader.py diff --git a/stock_alerter/tests/test_reader.py b/stock_alerter/tests/test_reader.py new file mode 100644 index 0000000..d6f76c5 --- /dev/null +++ b/stock_alerter/tests/test_reader.py @@ -0,0 +1,18 @@ +import unittest +from unittest import mock +from datetime import datetime + +from ..reader import FileReader + + +class FileReaderTest(unittest.TestCase): + @mock.patch("builtins.open", + mock.mock_open(read_data="""\ +GOOG,2014-02-11T14:10:22.13,10""")) + def test_FileReader_returns_the_file_contents(self): + reader = FileReader("stocks.txt") + updater = reader.get_updates() + update = next(updater) + self.assertEqual(("GOOG", + datetime(2014, 2, 11, 14, 10, 22, 130000), + 10), update) From 76b9873c8396c916120b1b65fd69635513b182ca Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Tue, 9 Jun 2015 09:07:29 +0530 Subject: [PATCH 20/21] Added documentation on how to use this repository --- README.md | 3 --- README.rst | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) delete mode 100644 README.md create mode 100644 README.rst diff --git a/README.md b/README.md deleted file mode 100644 index 8236bd9..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Test Driven Python Development - -This repository contains the code for the book Test Driven Python Development (Packt Publishing). The repository has branches, so you can get the code for the individual chapters by choossing the appropriate branch. Feel free to fork the repository to work on the code as you go through the book. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..a862618 --- /dev/null +++ b/README.rst @@ -0,0 +1,24 @@ +Test Driven Python Development +============================== + +This repository contains the code for the book `Test Driven Python Development `. + +How to use this repository +-------------------------- + +The repository has tags, so you can get the code for the individual chapters by choosing the appropriate tag. + +**If you are familiar with using git & github**: Clone this repository, then look at tags to get the point in the code you want to go to. Checkout the appropriate tag on your local repository to get to that particular state of the code. + +**If you have not used github before**: Look at the `list of tags `, and click the *zip link* for the point in the code that you are interested in. This will download the code for that particular commit as a zip file. Unzip this file into your project folder and work from there. + +Notes +----- + +* Python 2.6+ compatible version of the code is available on it's own branch here - https://github.com/siddhi/test_driven_python/tree/py2.6 +* Some readers have submitted contributions to this repository. You can access the code with these contributions in the *contrib* branch - https://github.com/siddhi/test_driven_python/tree/contrib + +Submitting contributions +------------------------ + +If you would like to submit changes to this repository, checkout the *contrib* branch, make changes and submit a pull request. I will be happy to merge the changes in. From 751e204b0b32b2780db598297217ff2758a9f39f Mon Sep 17 00:00:00 2001 From: Siddharta Govindaraj Date: Tue, 9 Jun 2015 09:09:08 +0530 Subject: [PATCH 21/21] Updated amazon link in documentation --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a862618..d3f83e6 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Test Driven Python Development ============================== -This repository contains the code for the book `Test Driven Python Development `. +This repository contains the code for the book `Test Driven Python Development `. How to use this repository --------------------------