1

I have a class with the following property clusters:

import numpy as np

class ClustererKmeans(object):

    def __init__(self):
        self.clustering = np.array([0, 0, 1, 1, 3, 3, 3, 4, 5, 5])

    @property
    def clusters(self):
        assert self.clustering is not None, 'A clustering shall be set before obtaining clusters'
        return np.unique(self.clustering)

I now want to write a unittest for this simple property. I start off with:

from unittest import TestCase, main
from unittest.mock import Mock

class Test_clusters(TestCase):

    def test_gw_01(self):
        sut = Mock()
        sut.clustering = np.array([0, 0, 1, 1, 3, 3, 3, 4, 5, 5])
        r = ClustererKmeans.clusters(sut)
        e = np.array([0, 1, 3, 4, 5])
        # The following line checks to see if the two numpy arrays r and e are equal,
        # and gives a detailed error message if they are not. 
        TestUtils.equal_np_matrix(self, r, e, 'clusters')

if __name__ == "__main__":
    main()

However, this does not run.

TypeError: 'property' object is not callable

I next change the line r = ClustererKmeans.clusters(sut) to the following:

r = sut.clusters

But again, I get an unexpected error.

AssertionError: False is not true : r shall be a <class 'numpy.ndarray'> (is now a <class 'unittest.mock.Mock'>)

Is there an easy way to test the implementation of a property in Python using the unittest framework?

2
  • 2
    Should you not be doing r = sut.clusters ? The self argument is sent by default. I see another issue sut.clustering is not the right way of initializing the class variable. You should be sending it as an argument while initializing the class Commented Jan 5, 2015 at 16:54
  • I did try r = sut.clusters, but in the code above, it returns a Mock object, not a numpy array. Commented Jan 6, 2015 at 6:08

1 Answer 1

3

To call property directly you can replace in your original code ClustererKmeans.clusters(sut) by ClustererKmeans.clusters.__get__(sut).

Even if I'm a mocking enthusiastic IMHO this case is not a good example to apply it. Mocking are useful to remove dependencies from class and resources. In your case ClustererKmeans have a empty constructor and there isn't any dependency to break. You can do it by:

class Test_clusters(TestCase):
    def test_gw_01(self):
        sut = ClustererKmeans()
        sut.clustering = np.array([0, 0, 1, 1, 3, 3, 3, 4, 5, 5])
        np.testing.assert_array_equal(np.array([0, 1, 2, 3, 4, 5]),sut.clusters)

If you would use mocking you can patch ClustererKmeans() object by using unittest.mock.patch.object:

def test_gw_01(self):
    sut = ClustererKmeans()
    with patch.object(sut,"clustering",new=np.array([0, 0, 1, 1, 3, 3, 3, 4, 5, 5])):
        e = np.array([0, 1, 3, 4, 5])
        np.testing.assert_array_equal(np.array([0, 1, 2, 3, 4, 5]),sut.clusters)

...but why use patch when python give to you a simple and direct way to do it?

Another way to use mock framework should be trust numpy.unique and check if the property do the right work:

@patch("numpy.unique")
def test_gw_01(self, mock_unique):
    sut = ClustererKmeans()
    sut.clustering = Mock()
    v = sut.clusters
    #Check is called ....
    mock_unique.assert_called_with(sut.clustering)
    #.... and return
    self.assertIs(v, mock_unique.return_value)

    #Moreover we can test the exception
    sut.clustering = None
    self.assertRaises(Exception, lambda s:s.clusters, sut)

I apologize for some errors but I don't test the code. I you notify to me I will fix all as soon as possible.

Sign up to request clarification or add additional context in comments.

3 Comments

The line r = ClustererKmeans.clusters(__get__(sut)) gives me an error: NameError: name '__get__' is not defined. The constructor is empty here, since I left out all irrelevant details in my question. In practice, the code is more elaborate.
The line r = ClustererKmeans.clusters.__get__(sut) does work as expected. Thanks for pointing this out!

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.