Set constructor keyworded arguments to mock attributes in Python

I'm unit testing a class that uses peewee for ORM. I used mock for testing and tried to make sure I set the correct values for the model's attributes, but what I set for the model's constructor. When passing as an argument with a keyword, the mock.Mock class could not be tested as it is, so I created a mock that sets the argument with a keyword to the attribute.

It's only two months old in Python, and you may have made a big mistake, and there may be a smarter way to do it. If anyone knows any mistakes or better ways, it would be greatly appreciated if you could comment.

2016/10/15 postscript The code in this article only supports the case where only one instance of the mock is created. In the case of multiple instances, you need a method like @ podhmo's exemplified code in the comment.

environment

Preparation

$ pip install peewee
$ pip install mock

Directory structure

$ tree
.
├── src
│   ├── __init__.py
│   ├── model.py
│   └── service.py
└── test
    ├── __init__.py
    └── test_service.py

Code to be tested

src/service.py


# -*- coding: utf-8 -*-

from model import HogeModel

class HogeService(object):
    def run(self):
        model = HogeModel(name='hoge name')
        model.desc = 'hoge desc'
        model.save()

src/model.py


# -*- coding: utf-8 -*-

from peewee import SqliteDatabase
from peewee import Model
from peewee import PrimaryKeyField
from peewee import CharField

db = SqliteDatabase('hoge.db')

class HogeModel(Model):
    id = PrimaryKeyField
    name = CharField()
    desc = CharField()

    class Meta:
        database = db

Test code

test/test_service.py


# -*- coding: utf-8 -*-

import unittest
from mock import patch
from mock import Mock
from service import HogeService

class TestHogeService(unittest.TestCase):

    @patch('service.HogeModel')
    def test_run(self, mock_hoge_model):
        mock = Mock()
        mock_hoge_model.return_value = mock
        target = HogeService()
        target.run()
        self.assertEquals(1, mock.save.call_count)  # OK
        self.assertEquals('hoge desc', mock.desc)  # OK
        self.assertEquals('hoge name', mock.name)  # NG

if __name__ == '__main__':
    unittest.main()
$ PYTHONPATH=src python test/test_service.py 
F
======================================================================
FAIL: test_run (__main__.TestHogeService)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/otti/.pyenv/versions/2.7.12/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "test/test_service.py", line 18, in test_run
    self.assertEquals('hoge name', mock.name)  # NG
AssertionError: 'hoge name' != <Mock name='HogeModel().name' id='4454023888'>

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

Set the keyworded arguments of the constructor to the mock attributes

test/test_service.py


# -*- coding: utf-8 -*-

import unittest
from mock import Mock
from mock import patch
from service import HogeService

class TestHogeService(unittest.TestCase):

    def create_attr_set_mock(self, **kwargs):
        self.mock = Mock()
        #Set the keyword argument passed in the constructor to the attribute
        for k, v in kwargs.items():
            self.mock.__dict__[k] = v
        return self.mock

    @patch('service.HogeModel')
    def test_run(self, mock_hoge_model):
        #Returns a mock that sets the constructor's keyworded arguments to the attribute when instantiating the target class
        mock_hoge_model.side_effect = lambda **kwargs: self.create_attr_set_mock(**kwargs)
        target = HogeService()
        target.run()
        self.assertEquals(1, self.mock.save.call_count) # OK
        self.assertEquals('hoge desc', self.mock.desc) # OK
        self.assertEquals('hoge name', self.mock.name) # OK

if __name__ == '__main__':
    unittest.main()
 PYTHONPATH=src python test/test_service.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Now you have a mock of the model that works as expected. It was a bit confusing because mock.Mock seems to have an attribute called name in the first place, but it was the same when I tried it with attributes other than name.

reference

Recommended Posts

Set constructor keyworded arguments to mock attributes in Python
How to receive command line arguments in Python
How to specify attributes with Mock of python
I want to write in Python! (3) Utilize the mock
To flush stdout in Python
Login to website in Python
Receive runtime arguments in Python 2.7
Speech to speech in python [text to speech]
How to develop in Python
Post to Slack in Python
Set python test in jenkins
How to import Python library set up in EFS to Lambda
Things to watch out for when using default arguments in Python
[Python] How to do PCA in Python
How to pass arguments to a Python script in SPSS Modeler Batch
Included notation in Python function arguments
Convert markdown to PDF in Python
How to collect images in Python
Set opset to embed in ONNX
How to use SQLite in Python
In the python command python points to python3.8
Try to calculate Trace in Python
How to use Mysql in python
How to wrap C in Python
How to use ChemSpider in Python
6 ways to string objects in Python
How to use PubChem in Python
Pass arguments to Task in discord.py
Precautions when giving default values to arguments in Python function definitions
How to handle Japanese in Python
An alternative to `pause` in Python
[Python] I tried to summarize the set type (set) in an easy-to-understand manner.
I tried to implement PLSA in Python
[Introduction to Python] How to use class in Python?
Try logging in to qiita with Python
Install Pyaudio to play wave in python
How to access environment variables in Python
I tried to implement permutation in Python
Method to build Python environment in Xcode 6
How to dynamically define variables in Python
How to do R chartr () in Python
Pin current directory to script directory in Python
[Itertools.permutations] How to put permutations in Python
PUT gzip directly to S3 in Python
Send email to multiple recipients in Python (Python 3)
Convert psd file to png in Python
Sample script to trap signals in Python
Decorator to avoid UnicodeEncodeError in Python 3 print ()
How to work with BigQuery in Python
Associate the table set in python models.py
Log in to Slack using requests in Python
How to get a stacktrace in python
How to display multiplication table in python
Easy way to use Wikipedia in Python
How to extract polygon area in Python
How to check opencv version in python
I tried to implement ADALINE in Python
[Python] Pandas to fully understand in 10 minutes
Throw Incoming Webhooks to Mattermost in Python
Module to generate word N-gram in Python
To reference environment variables in Python in Blender