[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Avoid converting functions to methods in a class

Steven D'Aprano

2/20/2010 3:34:00 AM

I have a convention when writing unit tests to put the target of the test
into a class attribute, as follows:

class MyTest(unittest.TestCase):
target = mymodule.someclass

def test_spam(self):
"""Test that someclass has a spam attribute."""
self.failUnless(hasattr(self.target, 'spam'))


It works well until I write a test for stand-alone functions:

class AnotherTest(unittest.TestCase):
target = mymodule.function

def test_foo(self):
self.assertEquals(self.target('a', 'b'), 'foo')

The problem is that target is turned into a method of my test class, not
a standalone function, and I get errors like:

TypeError: function() takes exactly 2 arguments (3 given)

The solution I currently use is to drop the target attribute in this
class, and just refer to mymodule.function in each individual test. I
don't like this solution because it violates Once And Only Once: if the
function changes name, I have to make many edits to the test suite rather
than just one.

Are there any better solutions?


--
Steven
4 Answers

Ben Finney

2/20/2010 4:56:00 AM

0

Steven D'Aprano <steve@REMOVE-THIS-cybersource.com.au> writes:

> I have a convention when writing unit tests

Incidentally, you may be interested in the specific forum for testing in
Python <URL:http://lists.idyll.org/listinfo/testing-in-... that is a
good resource for asking questions like this.

> to put the target of the test into a class attribute, as follows:
>
> class MyTest(unittest.TestCase):
> target = mymodule.someclass
>
> def test_spam(self):
> """Test that someclass has a spam attribute."""
> self.failUnless(hasattr(self.target, 'spam'))
>
>
> It works well until I write a test for stand-alone functions:
>
> class AnotherTest(unittest.TestCase):
> target = mymodule.function
>
> def test_foo(self):
> self.assertEquals(self.target('a', 'b'), 'foo')

I think this is because you're binding the â??targetâ?? attribute at the
class definition, binding it to that class.

Instead, this smells like something that should be a fixture for each
test case::

class SomeClassTestCase(unittest.TestCase):

def setUp(self):
""" Set up test fixtures. """
self.target = mymodule.SomeClass

def test_has_spam(self):
""" Should have a spam attribute. """
self.failUnless(hasattr(self.target, 'spam'))

def test_has_no_bacon(self):
""" Should not have a bacon attribute. """
self.failIf(hasattr(self.target, 'bacon'))


class SomeFunctionTest(unittest.TestCase):

def setUp(self):
""" Set up test fixtures. """
self.target = mymodule.some_function

def test_a_b_input_returns_foo(self):
""" Should return 'foo' from 'a', 'b' inputs. """
self.assertEquals(self.target('a', 'b'), 'foo')

def test_c_d_input_raises_valueerror(self):
""" Should raise ValueError from 'c', 'd' inputs. """
self.assertRaises(
ValueError,
self.target, 'c', 'd')

--
\ â??A free press is one where it's okay to state the conclusion |
`\ you're led to by the evidence.â? â??Bill Moyers |
_o__) |
Ben Finney

Arnaud Delobelle

2/20/2010 11:01:00 AM

0

On 20 Feb, 03:33, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote:
> I have a convention when writing unit tests to put the target of the test
> into a class attribute, as follows:
>
> class MyTest(unittest.TestCase):
>     target = mymodule.someclass
>
>     def test_spam(self):
>         """Test that someclass has a spam attribute."""
>         self.failUnless(hasattr(self.target, 'spam'))
>
> It works well until I write a test for stand-alone functions:
>
> class AnotherTest(unittest.TestCase):
>     target = mymodule.function
>
>     def test_foo(self):
>         self.assertEquals(self.target('a', 'b'), 'foo')
>
> The problem is that target is turned into a method of my test class, not
> a standalone function, and I get errors like:
>
> TypeError: function() takes exactly 2 arguments (3 given)
>
> The solution I currently use is to drop the target attribute in this
> class, and just refer to mymodule.function in each individual test. I
> don't like this solution because it violates Once And Only Once: if the
> function changes name, I have to make many edits to the test suite rather
> than just one.
>
> Are there any better solutions?
>
> --
> Steven

Why not define target in the TestCase.setUp() method?

class AnotherTest(unittest.TestCase):

def setUp(self):
self.target = mymodule.function

def test_foo(self):
self.assertEquals(self.target('a', 'b'), 'foo')

--
Arnaud



class
--
Arnaud

Jean-Michel Pichavant

2/23/2010 12:50:00 PM

0

Steven D'Aprano wrote:
> I have a convention when writing unit tests to put the target of the test
> into a class attribute, as follows:
>
> class MyTest(unittest.TestCase):
> target = mymodule.someclass
>
> def test_spam(self):
> """Test that someclass has a spam attribute."""
> self.failUnless(hasattr(self.target, 'spam'))
>
>
> It works well until I write a test for stand-alone functions:
>
> class AnotherTest(unittest.TestCase):
> target = mymodule.function
>
> def test_foo(self):
> self.assertEquals(self.target('a', 'b'), 'foo')
>
> The problem is that target is turned into a method of my test class, not
> a standalone function, and I get errors like:
>
> TypeError: function() takes exactly 2 arguments (3 given)
>
> The solution I currently use is to drop the target attribute in this
> class, and just refer to mymodule.function in each individual test. I
> don't like this solution because it violates Once And Only Once: if the
> function changes name, I have to make many edits to the test suite rather
> than just one.
>
> Are there any better solutions?
>
>
>

It looks like it works when declaring foo as static:

import unittest

def foo(a,b):
return 'fooo'

class AnotherTest(unittest.TestCase):
target = staticmethod(foo)

def test_foo(self):
self.assertEquals(self.target('a', 'b'), 'foo')

def runTest(self):
self.test_foo()

AnotherTest().runTest()
....AssertionError: 'fooo' != 'foo'

JM

Daniel Fetchinson

2/23/2010 3:39:00 PM

0

> I have a convention when writing unit tests to put the target of the test
> into a class attribute, as follows:
>
> class MyTest(unittest.TestCase):
> target = mymodule.someclass
>
> def test_spam(self):
> """Test that someclass has a spam attribute."""
> self.failUnless(hasattr(self.target, 'spam'))
>
>
> It works well until I write a test for stand-alone functions:
>
> class AnotherTest(unittest.TestCase):
> target = mymodule.function
>
> def test_foo(self):
> self.assertEquals(self.target('a', 'b'), 'foo')
>
> The problem is that target is turned into a method of my test class, not
> a standalone function, and I get errors like:
>
> TypeError: function() takes exactly 2 arguments (3 given)
>
> The solution I currently use is to drop the target attribute in this
> class, and just refer to mymodule.function in each individual test. I
> don't like this solution because it violates Once And Only Once: if the
> function changes name, I have to make many edits to the test suite rather
> than just one.

Isn't staticmethod the trick you need?

HTH,
Daniel


--
Psss, psss, put it down! - http://www.cafepress.com...