[Tutor] Proper way to unit test the raising of exceptions?

2018-04-01 Thread Simon Connah via Tutor
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?

2018-04-01 Thread Steven D'Aprano
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?

2018-04-01 Thread Peter Otten
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?

2018-04-01 Thread Mats Wichmann
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?

2018-04-01 Thread Simon Connah via Tutor
 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

2018-04-01 Thread Albert-Jan Roskam

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

2018-04-01 Thread Alan Gauld via Tutor
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

2018-04-01 Thread Steven D'Aprano
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