Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have a base class that defines a class attribute and some child classes that depend on it, e.g.

class Base(object):
    assignment = dict(a=1, b=2, c=3)

I want to unittest this class with different assignments, e.g. empty dictionary, single item, etc. This is extremely simplified of course, it's not a matter of refactoring my classes or tests

The (pytest) tests I have come up with, eventually, that work are

from .base import Base

def test_empty(self):
    with mock.patch("base.Base.assignment") as a:
        a.__get__ = mock.Mock(return_value={})
        assert len(Base().assignment.values()) == 0

def test_single(self):
    with mock.patch("base.Base.assignment") as a:
        a.__get__ = mock.Mock(return_value={'a':1})
        assert len(Base().assignment.values()) == 1

This feels rather complicated and hacky - I don't even fully understand why it works (I am familiar with descriptors though). Does mock automagically transform class attributes into descriptors?

A solution that would feel more logical does not work:

def test_single(self):
    with mock.patch("base.Base") as a:
        a.assignment = mock.PropertyMock(return_value={'a':1})
        assert len(Base().assignment.values()) == 1

or just

def test_single(self):
    with mock.patch("base.Base") as a:
        a.assignment = {'a':1}
        assert len(Base().assignment.values()) == 1

Other variants that I've tried don't work either (assignments remains unchanged in the test).

What's the proper way to mock a class attribute? Is there a better / more understandable way than the one above?

question from:https://stackoverflow.com/questions/22324628/better-way-to-mock-class-attribute-in-python-unit-test

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
323 views
Welcome To Ask or Share your Answers For Others

1 Answer

base.Base.assignment is simply replaced with a Mock object. You made it a descriptor by adding a __get__ method.

It's a little verbose and a little unnecessary; you could simply set base.Base.assignment directly:

def test_empty(self):
    Base.assignment = {}
    assert len(Base().assignment.values()) == 0

This isn't too safe when using test concurrency, of course.

To use a PropertyMock, I'd use:

with patch('base.Base.assignment', new_callable=PropertyMock) as a:
    a.return_value = {'a': 1}

or even:

with patch('base.Base.assignment', new_callable=PropertyMock, 
           return_value={'a': 1}):

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...