1

Trying to understand mocking/patching and I have a restful API project with three files (FYI, I'm using flask)

  1. class1.py
  2. domain.py
  3. test_domain.py

class1.py file content:

class one:
    def addition(self):
       return 4+5

domain.py file content:

from class1 import one

class DomainClass(Resource):

    def post(self):

        test1 = one()
        val = test1.addition()

        return {'test' : val } 

test_domain.py file content:

import my_app
from flask_api import status
from mock import patch

app = my_app.app.test_client()

def test_post():
    with patch('domain.one') as mock:
        instance = mock.return_value
        instance.addition.return_value = 'yello'

    url = '/domain'
    response = app.post(url)
    print response.data

    assert status.HTTP_200_OK == response.status_code
    assert mock.called

For my test_domain.py file, I've also tried this...

@patch('domain.one')
def test_post(mock_domain):
    mock_domain.addition.return_value = 1

    url = '/domain'
    response = app.post(url)
    print response.data

    assert status.HTTP_200_OK == response.status_code

My assert for the status of 200 passes, however, the problem is that I'm not able to mock or patch the addition method to give me value of 1 in place of 9 (4+5). I also tried doing 'assert mock.called' and it failes as well. I know I should be mocking/patching where the 'one()' method is used, i.e. in domain.py not in class1.py. But I tried even mocking class1.one in place of domain.one and I still kept getting 9 and not 1. What am I doing wrong ?

******** Update I've another dilemma on the same issue, I tried doing this in the test_domain file instead of patching....

from common.class1 import one
def test_post():
    one.addition = MagicMock(return_value=40)

    url = '/domain'
    response = app.post(url)
    print response.data

    assert status.HTTP_200_OK == response.status_code

Question

  1. In update above, I did not do a mock at the place where it is used (i.e.: domain.one.addition = MagicMock(...) and it still worked !!!! It seems it may be doing a global change. Why did this work ?

  2. In the above example, 'one' is a class in the module class1.py. If I change this class 'one' to a function in class1.py, mocking does not work. It seems this function 'one' residing in module class1.py can not be mocked like this...one.return_value = 'xyz', why? Can it be mocked globally ?

LuckyStarr
  • 1,465
  • 2
  • 12
  • 14
  • In your first example of `test_post()` the code from `url = '/domain'` to `assert mock.called` should be correctly indented in the `with` context. It is just a typo error or it is a real issue in your code? – Michele d'Amico May 07 '15 at 07:26

1 Answers1

2

There are some issues in your code. In the first example you forgot that patch() is applied in with context and the original code is recovered when the context end. Follow code should work:

def test_post():
    with patch('domain.one') as mock:
        instance = mock.return_value
        instance.addition.return_value = 'yello'
        url = '/domain'
        response = app.post(url)
        print response.data
        assert status.HTTP_200_OK == response.status_code
        assert mock.called
        assert response.data['test'] == 'yello'

The second one have an other issue: if you want patch just addition method you should use:

@patch('domain.one.addition')
def test_post(mock_addition):
    mock_addition.return_value = 1
    ...
    assert mock_addition.called
    assert response.data['test'] == 1

If you want mock all one class you should set the return value of addition method of mock instance returned by mock_domain call like in your first example:

@patch('domain.one')
def test_post(mock_domain):
    mock_addition = mock_domain.return_value.addition
    mock_addition.return_value = 1
    ...
    assert mock_addition.called
    assert response.data['test'] == 1
Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
  • Thanks a bunch, I had figured how the decorator solution worked but not the one with 'with'. – LuckyStarr May 07 '15 at 13:14
  • @ParindShah The one with `with` is the simplest one... You had already write the right code but **all your test should be in `with` context** otherwise when you exit from `with` the original not mocked code is recovered and your test will use the no mocked `one` class. – Michele d'Amico May 07 '15 at 13:27
  • Thank u much. I knew this cork with 'with' very much but seemed to forgot, thanks for reminding. However, I have posted a new update on the question. I would appreciate any thought on these two new questions. Also, any thoughts on when to use a patch and when to use a mock ? – LuckyStarr May 11 '15 at 02:46
  • @ParindShah Please remove your edit part from the question (don't edit a question if you have an accepted answer yet... make the question just more confused). If you have an other question post a new one and use the link to the original one if it is relevant to understand the newer. Anyway I can answer to the new topics: take care that you are working with **reference** and not **copy**: by `from a import b` you create a a new **reference** to `b` in new module and by change `b.something` you are changing the **refence** of `b.something` in the origina class definition. – Michele d'Amico May 11 '15 at 07:02