1

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):
..... 
4
  • 5
    Why don't you move the logic for reading data from a file out to a class method, so you'd call e.g. MyClass.from_file(whatever_file) rather than MyClass(whatever_file)? Commented May 14, 2015 at 13:42
  • 1
    please post your code, this sounds like it will benefit from a redesign to allow it to be testable? Commented May 14, 2015 at 13:42
  • Can't post the code sorry non disclosure but I'll write a sample version up in a few mins Commented May 14, 2015 at 13:43
  • @user3413046 if you can't even tell us enough to explain why the current structure is so essential, this is a very difficult question to answer. Commented May 14, 2015 at 15:38

3 Answers 3

1

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'
Sign up to request clarification or add additional context in comments.

3 Comments

Sorry I'm still traveling , no access to a pc right now, do the instances methods still work as you mentioned setting the members is to be done manually?
Hello. I think that my example may answer your question. Let me know if it doesn't. Thanks.
No problem. It was a neat little trick I ran across in a great book called Python Cookbook 3rd Edition by O'Reilly press. You can read it for free at the link below, but I highly recommend purchasing a hard copy. It's killer. chimera.labs.oreilly.com/books/1230000000393
0

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).

Comments

0

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.

2 Comments

Thanks I appreciate the detailed answer but unfortunately I can't avoid loading records outside the constructor. Not my decision to do that but is there any work around to suppress exceptions?
Of course. Like I suggested, look at mock module or pytest fixtures. Both can be a little complicated for a beginner though, so good luck. You can also use docs.python.org/2/library/tempfile.html to provide real files for input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.