Robert Smith
Robert Smith

Reputation: 309

Asserting an exception generated by a @propertyname.setter

The Source Code

I have a bit of code requiring that I call a property setter to test wether or not locking functionaliy of a class is working (some functions of the class are async, requiring that a padlock boolean be set during their execution). The setter has been written to raise a RuntimeError if the lock has been set for the instance.

Here is the code:

    @filename.setter
    def filename(self, value):
        if not self.__padlock:
            self.__filename = value
        else:
            self.__events.on_error("data_store is locked.  you should be awaiting safe_unlock if you wish to "
                                  "change the source.")

As you can see here, if self.__padlock is True, a RuntimeError is raised. The issue arises when attempting to assert the setter with python unittest .

The Problem

It appears that unittest lacks functionality needed to assert wether or not a property setter raises an exception.

Attempting to use assertRaises doesn't obviously work:

    # Non-working concept
    self.assertRaises(RuntimeError, my_object.filename, "testfile.txt")

The Question

How does one assert that a class's property setter will raise a given exception in a python unittest.TestCase method?

Upvotes: 2

Views: 594

Answers (2)

SlimBeji
SlimBeji

Reputation: 9

You can use the setattr method like so:

self.assertRaises(ValueError, setattr, p, "name", None)

In the above example, we will try to set p.name equal to None and check if there is a ValueError raised.

Upvotes: 1

chepner
chepner

Reputation: 531938

You need to actually invoke the setter, via an assignment. This is simple to do, as long as you use assertRaises as a context manager.

with self.assertRaises(RuntimeError):
    my_object.filename = "testfile.txt"

If you couldn't do that, you would have to fall back to an explicit try statement (which gets tricky, because you need to handle both "no exception" and "exception other than RuntimeError" separately.

try:
    my_object.filename = "testfile.txt"
except RuntimeError:
    pass
except Exception:
    raise AssertionError("something other than RuntimeError")
else:
    raise AssertionError("no RuntimeError")

or (more) explicitly invoke the setter:

self.assertRaises(RuntimeError, setattr, myobject, 'filename', 'testfile.txt')

or worse, explicitly invoke the setter:

self.assertRaises(RuntimeError, type(myobject).filename.fset, myobject, 'testfile.txt')

In other words, three cheers for context managers!

Upvotes: 6

Related Questions