[Tutor] Proper way to unit test the raising of exceptions?
Hi, I'm just wondering what the accepted way to handle unit testing exceptions is? I know you are meant to use assertRaises, but my code seems a little off. try: some_func() except SomeException: self.assertRaises(SomeException) Is there a better way to do this at all? The problem with the above code is that if no exception is raised the code completely skips the except block and that would mean that the unit test would pass so, I considered adding: self.fail('No exception raised') at the end outside of the except block but not sure if that is what I need to do. Any help is appreciated. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Proper way to unit test the raising of exceptions?
On Sun, Apr 01, 2018 at 10:57:15AM +, Simon Connah via Tutor wrote: > I'm just wondering what the accepted way to handle unit testing > exceptions is? I know you are meant to use assertRaises, but my code > seems a little off. Here's a modified example from the statistics library in Python 3.4 and above, testing that statistics.mean raises appropriate errors. class TestMean(TestCase): def test_no_args(self): # Fail if given no arguments. self.assertRaises(TypeError, statistics.mean) def test_empty_data(self): # Fail when the data argument (first argument) is empty. for empty in ([], (), iter([])): self.assertRaises(statistics.StatisticsError, statistics.mean, empty) Let's dissect how they work. `test_no_args` calls self.assertRaises(TypeError, statistics.mean) which calls `statistics.mean` with no arguments. If it raises TypeError, then the test passes; otherwise the test fails. `test_empty_data` makes the following test: self.assertRaises(statistics.StatisticsError, statistics.mean, []) which calls `statistics.mean` with a single argument, the empty list [], and fails if StatisticsError is NOT raised; then it repeats the test using an empty tuple (), and a third time with an empty iterable. Only if all three tests raise does the test pass. Examples are heavily modified from here: https://github.com/python/cpython/blob/3.7/Lib/test/test_statistics.py > try: some_func() > except SomeException: self.assertRaises(SomeException) Re-write it as: self.assertRaises(SomeException, some_func) Note carefully that you do NOT write some_func() with parentheses. The idea is to pass the function object itself to the test case, which will then call it for you. If your function needs arguments, you add them after the function: self.assertRaises(SomeException, some_func, 1, 2, 3, "surprise") will call some_func(1, 2, 3, "surprise") and only pass if it raises SomeException. -- Steve ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Proper way to unit test the raising of exceptions?
Simon Connah via Tutor wrote: > Hi, > I'm just wondering what the accepted way to handle unit testing exceptions > is? I know you are meant to use assertRaises, but my code seems a little > off. > try: > some_func() > except SomeException: > self.assertRaises(SomeException) The logic is wrong here as you surmise below. If you catch the exception explicitly you have to write try: some_func() except SomeException: pass # OK else: self.fail("no exception raised") Alternatively you can write self.assertRaises(SomeException, some_func) which is already better, but becomes a bit hard to read once some_func takes arguments: self.assertRaises(SomeException, some_func, "one", two=42) Therefore I recommend using assertRaises() as a context manager: with self.assertRaises(SomeException): some_func("one", two=42) This has the advantage that some_func() appears with its args as it would in normal code, and that you can also test expressions: with self.assertRaises(ZeroDivisionError): MyNumber(1)/0 > Is there a better way to do this at all? > The problem with the above code is that if no exception is raised the code > completely skips the except block and that would mean that the unit test > would pass so, I considered adding: self.fail('No exception raised') at > the end outside of the except block but not sure if that is what I need to > do. Any help is appreciated. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Proper way to unit test the raising of exceptions?
On 04/01/2018 09:10 AM, Peter Otten wrote: > Simon Connah via Tutor wrote: > >> Hi, >> I'm just wondering what the accepted way to handle unit testing exceptions >> is? I know you are meant to use assertRaises, but my code seems a little >> off. > >> try: >> some_func() >> except SomeException: >> self.assertRaises(SomeException) > > The logic is wrong here as you surmise below. If you catch the exception > explicitly you have to write > > try: > some_func() > except SomeException: > pass # OK > else: > self.fail("no exception raised") If you use PyTest, the procedure is pretty well documented: https://docs.pytest.org/en/latest/assert.html ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Proper way to unit test the raising of exceptions?
Awesome. Thank you all. Your solutions are great and should make the whole process a lot more simple. The only problem is that some_func() on my end is Django model with about 8 named arguments so it might be a bit of a pain passing all of those arguments. The context manager example seems like a perfect fit for that particular problem. Thanks again. All of your help is much appreciated. On Sunday, 1 April 2018, 16:32:11 BST, Mats Wichmann wrote: On 04/01/2018 09:10 AM, Peter Otten wrote: > Simon Connah via Tutor wrote: > >> Hi, >> I'm just wondering what the accepted way to handle unit testing exceptions >> is? I know you are meant to use assertRaises, but my code seems a little >> off. > >> try: >> some_func() >> except SomeException: >> self.assertRaises(SomeException) > > The logic is wrong here as you surmise below. If you catch the exception > explicitly you have to write > > try: > some_func() > except SomeException: > pass # OK > else: > self.fail("no exception raised") If you use PyTest, the procedure is pretty well documented: https://docs.pytest.org/en/latest/assert.html ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] pythonic
On Mar 30, 2018 10:39, Alan Gauld via Tutor wrote: > > On 30/03/18 03:48, Pat Martin wrote: > > > the "right" way to do it in python? > > More or less, a couple of comments below... > > > def Main(): > > Python function names begin with a lowercase letter by convention. > > > """Run if run as a program.""" > > parser = argparse.ArgumentParser() > ... > > > > > now = datetime.datetime.now() > > slug = args.title.replace(" ", "-").lower() > > > > with open("{}.md".format(slug), 'w') as f: > > f.write("Title: {}\n".format(args.title)) > > f.write("Date: {}-{}-{} {}:{}\n".format(now.year, > > now.month, > > now.day, > > now.hour, > > now.minute)) > > Formatting of dates and times is usually done using > the time.strftime function which is specifically > designed for that. It might be worth taking a peek > at the docs on that one. You can call it directly > on a datetime object - 'now' in your case: > > fmt="%Y-%m-%d %H:%M\n" > f.write(now.strftime(fmt)) Lately I've been using format(), which uses __format__, because I find it slightly more readable: format(datetime.now(), "%Y-%m-%d %H:%M") ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] pythonic
On01/04/18 20:20, Albert-Jan Roskam wrote: > fmt="%Y-%m-%d %H:%M\n" > f.write(now.strftime(fmt)) > Lately I've been using format(), which uses __format__, because I find it > slightly more readable: > format(datetime.now(), "%Y-%m-%d %H:%M") Interesting, I didn't know that format() recognised the datetime format codes. I assumed it would just use the printf codes. Time to do some more reading I see. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] pythonic
On Sun, Apr 01, 2018 at 10:58:51PM +0100, Alan Gauld via Tutor wrote: > On01/04/18 20:20, Albert-Jan Roskam wrote: > > fmt="%Y-%m-%d %H:%M\n" > > f.write(now.strftime(fmt)) > > Lately I've been using format(), which uses __format__, because I find it > > slightly more readable: > > format(datetime.now(), "%Y-%m-%d %H:%M") > Interesting, > I didn't know that format() recognised the datetime format codes. It doesn't. It is the datetime object that recognises them. format() merely passes the format string to the datetime.__format__ method, which is what recognises the codes. It doesn't care what it is. py> class Spam: ... def __format__(self, template): ... return template.replace("^DD^", " surprise ") ... py> format(Spam(), "some^DD^string") 'some surprise string' -- Steve ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor