user3413046
user3413046

Reputation: 109

Construct object via __init__ and ignore constructor exception

I have a Python class whose __init__ method raises a custom exception called WrongFileSpecified.

However, when I write a unit test, I want to assign the attributes of the instance object from a test fixture. So normally what I would be doing is reading data off a file and then working with the instance object.

But with the test, I cannot use any test files, so I basically need to hard code the data in the instance object in the setUp method of the unit test. Is there any way to get a instance created without __init__ complaining about the exception?

Sample code:

class A(object):
    def __init__(self, folderPath):
        #check folder path using os.isdir() otherwise raise exception
        #...
        self.folderPath = folderPath
        #Call load record
        self._load_records() #uses self.folderPath and raises exceptions as well

        #Note i cannot avoid raising these exceptions, its required

class TestA(unittest.TestCase):
   .......
   obj = None
   def setUp(self):
        obj = A('fake folder path')
        obj.val1 = "testparam1"
        obj.param2 = "testparam2"

   def test_1(self):
..... 

Upvotes: 1

Views: 1598

Answers (3)

Trekkie
Trekkie

Reputation: 994

You can create an empty object, bypassing __init__ by using __new__.

obj = obj_type.__new__(obj_type)

Note that obj_type is the appropriate type object. This is a little hacky but it works. You are reponsible for setting the object's members.

Edit: here is an example.

class Foo():
    def __init__(self):
        self.x = 1
        self.y = 2

    def say_hello(self):
        print('Hello!')

r = Foo.__new__(Foo)

r.say_hello()

print(r.x)

Console output:

Hello!
Traceback (most recent call last):
  File "C:\WinPython-64bit-3.3.5.7\python-
    3.3.5.amd64\Scripts\projects\luc_utils\dev\test\
unit_test_serialization.py", line 29, in <module>
print(r.x)
AttributeError: 'Foo' object has no attribute 'x'

Upvotes: 1

thule
thule

Reputation: 4232

There is a very useful module called mock, you can check it out later, I feel that in this case it will be too much. Instead, you should consider redesigning your class, like this, for example:

class A(object):

    def __init__(self, folderPath):
        self.folderPath = folderPath

    def _load_records(self)
        #check folder path using os.isdir() otherwise raise exception
        ...
        #uses self.folderPath and raises exceptions as well
        ...

    @classmethod
    def load_records(cls, folderpath):
        obj = cls(folderpath)
        obj._load_records()
        return obj

    # Usage
    records = A.load_records('/path/to/records')

Then you can do:

class TestA(unittest.TestCase):
   .......
   obj = None
   def setUp(self):
        self.obj = A('fake folder path')
        self.obj.val1 = "testparam1"
        self.obj.param2 = "testparam2"

   def test_1(self):
        self.assertRaises(self.obj._load_records, HorribleFailureError)

Also i highly recommend to check out pytest, it has wonderful facilities for test fixtures, including fixtures for files and folders.

Upvotes: 0

jonrsharpe
jonrsharpe

Reputation: 122116

Here are two options:

  1. Refactor the file loading out to a class method, which is the Pythonic method of providing an alternate constructor (see below); or
  2. Provide an additional parameter to __init__ to suppress the exceptions when necessary (e.g. def __init__(self, folderPath, suppress=False), or validate=True, whichever makes more sense for your usage).

The latter is a bit awkward, in my opinion, but would mean that you don't have to refactor existing code creating A instances. The former would look like:

class A(object):

    def __init__(self, ...):
        """Pass whatever is loaded from the file to __init__."""
        ...

    @classmethod
    def from_file(cls, folderPath):
        """Load the data from the file, or raise an exception."""
        ...

and you would replace e.g. a = A(whatever) with a = A.from_file(whatever).

Upvotes: 0

Related Questions