0

I am testing the generating mock exception using side_effect. based on this page https://changhsinlee.com/pytest-mock/ I am trying to generate the exception when calling load_data method, but its not working for me.

test_load_data.py::test_slow_load_02 FAILED                              [100%]
tests/pytest_samples/test_load_data.py:31 (test_slow_load_02)
ds_mock = <MagicMock name='DataSet' id='4473855648'>

    @patch("python_tools.pytest_samples.load_data.DataSet")
    def test_slow_load_02(ds_mock):
        with patch.object(ds_mock, 'load_data', side_effect=Exception('URLError')):
>           with pytest.raises(Exception) as excinfo:
E           Failed: DID NOT RAISE <class 'Exception'>

test_load_data.py:35: Failed

Here is the code.

slow.py

class DataSet:

    def load_data(self):
        return 'Loading Data'

load_data.py

from python_tools.pytest_samples.slow import DataSet


def slow_load():
    dataset = DataSet()
    return dataset.load_data()

test_data.py

@patch("python_tools.pytest_samples.load_data.DataSet")
def test_slow_load_02(ds_mock):
    with patch.object(ds_mock, 'load_data', side_effect=Exception('URLError')):
        with pytest.raises(Exception) as excinfo:
            actual = slow_load()
        assert str(excinfo.value) == 'URLError'

This test code worked for me:

def test_slow_load_01(mocker):
    with mocker.patch("python_tools.pytest_samples.load_data.DataSet.load_data", side_effect=Exception('URLError')):
        with pytest.raises(Exception) as excinfo:
            actual = slow_load()
        assert str(excinfo.value) == 'URLError'

I like to understand why patch and patch.object is not working.

Thanks

sfgroups
  • 18,151
  • 28
  • 132
  • 204
  • Does this answer your question? [Python unit tests - mocking imported modules / functions](https://stackoverflow.com/questions/75019865/python-unit-tests-mocking-imported-modules-functions) – aaron Jan 25 '23 at 17:50
  • It does with `@patch("python_tools.pytest_samples.load_data.DataSet.load_data", side_effect=Exception('URLError'))`. Can you update your question to show your changed code? – aaron Jan 26 '23 at 16:07
  • @aaron found the answer, posted the answer. Thanks – sfgroups Jan 28 '23 at 01:02

2 Answers2

0

This test code worked for me.

import pytest
from python_tools.pytest_samples import load_data

def test_slow_load_02():
    with patch.object(load_data.DataSet, 'load_data', side_effect=Exception('URLError')):
        with pytest.raises(Exception) as excinfo:
            actual = load_data.slow_load()
        assert str(excinfo.value) == 'URLError'
sfgroups
  • 18,151
  • 28
  • 132
  • 204
  • This isn't much different from `@patch("python_tools.pytest_samples.load_data.DataSet.load_data", side_effect=Exception('URLError'))` as alluded to in the duplicate target. – aaron Jan 28 '23 at 02:32
  • I think we should us `patch.object` in this case not `patch`. patch: replaces the object in the code being tested. patch.object: replaces an attribute of an object in the code being tested. – sfgroups Jan 29 '23 at 23:22
  • The attribute of an object is an object too. You're patching the exact same thing here -- the method `load_data`. – aaron Jan 30 '23 at 00:01
-1

You are patching the wrong thing; slow_load doesn't refer to the class using the expression python_tools.pytest_samples.slow.DataSet; it uses the global variable DataSet.

@patch('load_data.DataSet')
def test_slow_load_02(ds_mock):
    with patch.object(ds_mock, 'load_data', side_effect=Exception('URLError')):
        with pytest.raises(Exception) as excinfo:
            actual = slow_load()
        assert str(excinfo.value) == 'URLError'

You don't need patch.object; ds_mock.load_data is already a mock that you can configure directly.

@patch('load_data.DataSet')
def test_slow_load_02(ds_mock):
    ds_mock.load_data.side_effect = Exception('URLError')
    with pytest.raises(Exception) as excinfo:
        actual = slow_load()
    assert str(excinfo.value) == 'URLError'
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I tried this `@patch("python_tools.pytest_samples.load_data.DataSet") def test_slow_load_02(ds_mock): ds_mock.load_data.side_effect = Exception('URLError') with pytest.raises(Exception) as excinfo: actual = slow_load() assert str(excinfo.value) == 'URLError'` still getting the same error message. unsure what I am missing. – sfgroups Jan 08 '23 at 18:08
  • You didn't change the call to `@patch` like I suggested. It's `load_data.DataSet` you need to patch, not the `python_tools...` name. – chepner Jan 08 '23 at 19:41
  • when I use `@patch('load_data.DataSet')` decorator I get `ModuleNotFoundError: No module named 'load_data'` error. in my project my module name is `python_tool` and source file is in `pytest_sample` folder. so I need to use the full name `python_tools.pytest_samples.load_data.DataSet`. – sfgroups Jan 08 '23 at 23:58
  • I assume you are writing something like `from load_data import slow_load` so that you can call the function. Just add `import load_data` so that that module itself is bound to a name in the scope. – chepner Jan 09 '23 at 13:05
  • yes I am using this import `from load_data import slow_load`. most of my application written like this only. do you know why test case its not working with this improt? – sfgroups Jan 09 '23 at 13:32
  • Because, as you've seen, you never define the name `load_data` so you can't patch it. – chepner Jan 09 '23 at 13:39
  • if you look at `test_slow_load_01` test case. I was able to mock the `load_data` method using `mocker`. its not working only with `patch`. – sfgroups Jan 10 '23 at 02:25