Giter Site home page Giter Site logo

mockito-python's Introduction

Mockito is a spying framework originally based on the Java library with the same name. (Actually we invented the strict stubbing mode back in 2009.)

Install

pip install mockito

Quick Start

90% use case is that you want to stub out a side effect.

from mockito import when, mock, unstub

when(os.path).exists('/foo').thenReturn(True)

# or:
import requests  # the famous library
# you actually want to return a Response-like obj, we'll fake it
response = mock({'status_code': 200, 'text': 'Ok'})
when(requests).get(...).thenReturn(response)

# use it
requests.get('http://google.com/')

# clean up
unstub()

Read the docs

http://mockito-python.readthedocs.io/en/latest/

Development

I use rye, and if you do too: you just clone this repo to your computer, then run rye sync in the root directory. Finally, activate the virtualenv. (rye shell gives you a hint on how to do that.)

pytest

mockito-python's People

Contributors

avandierast avatar bbankowski avatar felixonmars avatar juicegit avatar kaste avatar lwoydziak avatar maximkulkin avatar nielsvaneck avatar oracking avatar shashankrnr32 avatar soplakanets avatar szczepiq avatar timvdlippe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockito-python's Issues

Add a method `thenCallRealMethod` to AnswerSelector to call the original method

Note: Please feel free to close the issue if this has been discussed before in any of the issue.

Currently the AnswerSelector interface provides the following methods to be used with when or when2 statement

  1. thenReturn: To return a mocked return value
  2. thenRaise: To raise a mock exception
  3. thenAnswer: To call a custom method (Eg: Lambdas)

I noticed the original method is stored in the __wrapped__ attribute when the method is stubbed.

new_mocked_method.__wrapped__ = original_method # type: ignore[attr-defined] # noqa: E501

This issue is to provide another option thenCallRealMethod to AnswerSelector to call this original method instead of mocking a stub value so that the original method get called instead of the mock. This will support "Partial Mocking" of these methods by allowing the user to call the original method in case the original method is not modifiable.

I have a small work here to support this (untested for now)

(I don't need this as such for my usecase, just some improvement!.)

Example:

class Dog(object):
    def bark(self, sound):
        return "%s!" % sound

rex = Dog()
when(rex).bark('Miau').thenReturn('Wuff').thenCallRealMethod().thenReturn('Puff')

assert 'Wuff' == rex.bark('Miau')
assert "Miau!" == rex.bark("Miau")     # Calls the original method
assert 'Puff' == rex.bark('Miau')

Also see: https://javadoc.io/static/org.mockito/mockito-core/4.1.0/org/mockito/stubbing/OngoingStubbing.html#thenCallRealMethod--

Thank you.

Add support for capturing all the arguments when there are multiple invocations

Hi @kaste

First of all, Thank you for this project. I have been using mockito-python for a while now and really like its features (and its similarity with Java framework).

I went through the issues in the repo and could not find one that is related to what my ask is. So creating this issue. If this was discussed before, please feel free to close it and link it as duplicate tagging the older issue.

Issue

Currently the ArgumentCaptor available in the matchers module allows capturing the method arguments. The limitation of this is that it just captures the last value when there are multiple invocations. For a usecase when a particular method is called multiple times with different values, its good to verify that the invocations were done with the correct arguments

Solution

There are 2 solutions I think of after going through the repo

  1. Add a new class called MultipleInvocationsArgumentCaptor that does this job. I also have an implementation ready (along with tests) for the same in this branch. I am happy to raise this as a PR. (Also, feel free to choose a new name for this class if its too long. ๐Ÿ˜„ )

  2. Modify the current ArgumentCaptor to take in multiple matchers and change certain aspects with this. This would be a backward incompatible change and I personally go with the 1st option keeping the current code as it is. But I added this option in case you want to maintain close similarity with Java framework, then this would be the preferred option.

As I said, I already have changes ready for the 1st solution I added(See branch). I will raise the PR once I get your response on the 1st solution. If you opt to go with the other option, I will be happy to work on it in my free time and raise a PR in a couple of days.

Thank you!

no attributes in dummy objects since 1.3.2

Normally I'd ask about this in Discussions, however they do not seem to be enabled for this project so I am misusing the issue. Since 1.3.2 (including) there has been some change in behavior.

In 1.3.0 everything is fine:

>>> import requests
>>> from mockito import mock
>>> m = mock(spec=requests.Response)
>>> m.status_code
functools.partial(<function remembered_invocation_builder at 0x107096790>, <mockito.mocking.Mock object at 0x1063ebe80>, 'status_code')
>>> 

however in 1.3.2 there was some change:

>>> import requests
>>> from mockito import mock
>>> m = mock(spec=requests.Response)
>>> m.status_code
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/private/tmp/venv/lib/python3.9/site-packages/mockito/mocking.py", line 286, in __getattr__
    raise AttributeError(
AttributeError: 'Dummy' has no attribute 'status_code' configured

which prevents the usual use cases. This is on Python 3.9.13 on macOS however happens also elsewhere.

new bug introduced/functionality removed in upgrade from 1.3 to 1.4

I just upgraded from mockito-1.3.0 to mockito-1.4.0 and many of my tests began to fail. Here is "somewhat minimal" example of what kind of code used to work, but it doesn't anymore (python 3.7.3):

from typing import Callable
from nose.tools import assert_equal
from mockito import mock, when


class _c(object):
    def __init__(self, fn: Callable[[int], str]) -> None:
        self._fn = fn

    def get_val(self, a: int) -> str:
        return self._fn(a)


def test_foo():
    x = mock()
    c = _c(x.get_value)
    when(x).get_value(10).thenReturn('foo')
    assert_equal(c.get_val(10), 'foo')

result:

$ nosetests a.py 
F
======================================================================
FAIL: a.test_foo
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/spurny/py-venv/wf3/lib/python3.7/site-packages/nose/case.py", line 198, in runTest
    self.test(*self.arg)
  File "/home/spurny/src-experiments/mockito-1.4-bug/a.py", line 18, in test_foo
    assert_equal(c.get_val(10), 'foo')
AssertionError: None != 'foo'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)

but if I mock the x.get_value before creating c:

...
def test_foo():
    x = mock()
    when(x).get_value(10).thenReturn('foo')  # moved here from below `c = _c(...` line
    c = _c(x.get_value)
    assert_equal(c.get_val(10), 'foo')

then it works fine:

$ nosetests a.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

resetting a mock?

Hi, thanks for this excellent library.

If I see correctly, there is no way of resetting a mock. Could you please add?
The closest thing I found is clear_invocations() in class Mock. But the examples always use mock(), not Mock().

Update: I guess I found it:

mockito.forget_invocations(my_mock)

Feature request: Mock current date/time

Add support for mocking the value returned from functions like datetime.datetime.now(). I have several functions that make use of the current date. I have not found a way to appropriately test those using Mockito.

The example below is how I have tried to mock the current date/time. I am not asking date/time freezing to work exactly like this example. This is just to help show what I am talking about.

Example

requirements.txt

expects==0.9.0
mamba==0.11.2
mockito==1.4.0

datetime_spec.py

from datetime import datetime

from expects import equal, expect
from mamba import description, before, after, it
from mockito import unstub, when

with description('current date') as self:
    with before.each:
        self.now = datetime(year=2024, month=1, day=1)
        when(datetime).now(...).thenReturn(self.now)

    with after.each:
        unstub()  # <-- This line throws an error

    with it('can mock the date'):
        expect(datetime.now().timestamp()).to(equal(1704067200.0))

Command

python -m mamba.cli ./datetime_spec.py

Error Message

Failure/Error: ./datetime_spec.py unstub()
         TypeError: cannot set 'now' attribute of immutable type 'datetime.datetime'

How to mock @property

When I have this:

import unittest

from mockito import unstub


class ClassName(object):

    @property
    def value(self):
        return 42

    def is_value(self):
        if self.value == 42:
            return 'is 42'
        raise ValueError()


class TestClassName(unittest.TestCase):

    def test_foo(self):
        x = ClassName()
        with self.assertRaises(ValueError):
            x.is_value()
        unstub()


if __name__ == '__main__':
    unittest.main()

Is there an easy to way to mock what value property returns?

Numpy arrays as when() arguments

When using a numpy array as an argument to when() a value error is raised with the following message:

"ValueError: The truth value of an array with more than one element is ambiguous."

which is caused by the compare function in invocation.py.

It would be good to be able to pass array types to when(), which would be evaluated using .all() during comparison.

Mypy integration

Related: #27

๐Ÿ‘‹ We currently are using mockito-python but checks are failing when running mypy.

Here's the output:

error: Skipping analyzing "mockito": module is installed, but missing library stubs or py.typed marker
note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

Is there an easy to make this work ?

Support one liner stubs?

Today I was trying to create a stub "factory" for requests.Response that supported the json() method and came up with the following snippet:

def jsonResponse(json):
    response = mock()
    when(response).json().thenReturn(json)
    return response

And I thought that it would have been much easier if I could do:

lambda json: when(response).json().thenReturn(json).mock()

I came up with a one liner but I had to dig into the code and it's undocumented territory:

lambda json: when(mock()).json().thenReturn(json).invocation.mock.mocked_obj

Would a method like mock() to return the original mocked obj be a feature aligned with the project?

Invocation of mocked @classmethod defined in super classes fails.

I recently tried mocking a class method and saw my test failing with a cryptic message
Fail: TypeError: missing a required argument: 'id' and an exception somewhere in the python library code.

I grabbed the mockito-python code, did some digging and found that the only thing that set my case apart from what mockito-python supports (and has tests for ๐Ÿ‘) is that in my case, the class method I tried mocking, was defined on a super class of the object in when().

Here's a little example that replicates the issue:

class LoudBarker:
    @classmethod
    def loud_bark(cls, bark):
        return bark

class Dog(LoudBarker):
    @classmethod
    def bark(cls):
        return "woof!"

class LoudBarkerTest(TestBase):
    def test_loud_dog(self):
        when(Dog).loud_bark("woof!").thenReturn("WOOOOF!")
        self.assertEqual(Dog.loud_bark("woof!"), "WOOOOF!") #fails with TypeError

Here, Dog.loud_bark("woof!") will fail with Fail: TypeError: missing a required argument: 'bark'.

If we write the mocking statement based on the class where the class method is defined, everything works as expected:

    def test_loud_bark__in_loud_barker(self):
        when(LoudBarker).loud_bark("woof!").thenReturn("WOOOOF!")
        self.assertEqual(Dog.loud_bark("woof!"), "WOOOOF!") #passes

In practice, this way of defining when() may be difficult because libraries do not always exactly expose in what class a class method is defined and source code may not always be available to find the answer.

How to use matchers/captor?

I'm not sure how to use matchers for the requests.put() function. I tried:

# verify
postArg = captor(ANY)
verify(requests, times=1).post(postArg, postArg, postArg)

I get the following error:

>       verify(requests, times=1).post(postArg, postArg, postArg)
E       mockito.verification.VerificationError: 
E       Wanted but not invoked:
E       
E           post(<ArgumentCaptor: matcher=<function any at 0x7f5242d2ec10> values=[]>, <ArgumentCaptor: matcher=<function any at 0x7f5242d2ec10> values=[]>, <ArgumentCaptor: matcher=<function any at 0x7f5242d2ec10> values=[]>)
E       
E       Instead got:
E       
E           post(headers={'url': 'https://habitica.com', 'x-api-user': 'cd18fc9f-b649-4384-932a-f3bda6fe8102', 'x-api-key': '18f22441-2c87-6d8e-fb2a-3fa670837b5a'}, url='https://habitica.com/api/v3/tasks/user/', data={'type': 'todo', 'text': 'Test task 1', 'date': '', 'alias': '8296278113', 'priority': '2', 'attribute': 'str'})

/home/user/Workspace/Habitica-Plus-Todoist/test/integration/oneWaySync_test.py:128: VerificationError

Support installation using Conda?

Hi there, I am really excited to use this library in Python. I have used it plenty of time in Java. Just one question, would installing through Conda environment be supported at some point? It would really simplify my Travis/Appveyor build.

Ellipsis in arg raises ValueError

I'm on mockito 1.1.0

File "mockito/signature.py" line 45 in match_signature
    if Ellipsis in args:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Here's some code that reproduces the bug:

import numpy as np
import unittest

from mockito import when, arg_that


class MakeMeFail:
    def run(self, array: np.ndarray) -> int:
        return 0


class TestMakeMeFail(unittest.TestCase):
    def setUp(self) -> None:
        self.__a_np_array = np.array([1, 2, 3])
        self.__make_me_fail = MakeMeFail()

    def test_this_one_fails(self) -> None:
        expected_value = -1029
        when(self.__make_me_fail)\
            .run(arg_that(lambda arg: np.array_equal(arg, self.__a_np_array)))\
            .thenReturn(expected_value)

        result = self.__make_me_fail.run(self.__a_np_array)

        self.assertEqual(expected_value, result)

    def test_it_is_caused_by_this(self) -> None:
        with self.assertRaises(ValueError):
            result = None in (self.__a_np_array, 1, 2)

    def test_this_one_works_because_kwargs_are_not_getting_the_Ellipsis_in_kwargs_check(self) -> None:
        expected_value = -1029
        when(self.__make_me_fail)\
            .run(array=arg_that(lambda arg: np.array_equal(arg, self.__a_np_array)))\
            .thenReturn(expected_value)

        result = self.__make_me_fail.run(array=self.__a_np_array)

        self.assertEqual(expected_value, result)

Love the module btw

"Called but not expected" with mocked open()

When I try to mock open() with mockito in python, I get the following error:

Called but not expected:

    open('/usr/lib/python3.9/__pycache__/doctest.cpython-39.pyc', 'rb')

Stubbed invocations are:

    open('oneWay_matchDict.pkl', 'rb')

The line to be mocked looks like this:

pkl_file = open('oneWay_matchDict.pkl','rb')

While the mock looks like this:

pkl_file = mock()
when2(open, 'oneWay_matchDict.pkl', 'rb').thenReturn(pkl_file)

How do I ignore other invocations of open?

Mocking sys.argv and sys.stdout like in patch

Is it possible to use Mockito to replace sys.argv or sys.stdout?

I could do it with unittest.mock.patch but I'd rather use a single mocking tool:

def it_outputs_answers_based_on_an_argument(when):
    with patch('sys.argv', ['cli.py', 'all']):
        when('app.cli.ApiClient').search_answers('all').thenReturn([
            {
                'id': 1,
                'question': 'A?',
                'content': 'B'
            }
        ])

        with patch('sys.stdout', StringIO()) as stdout:
            main()
            output = stdout.getvalue().strip()
            expect(output).to(equal('A?\n--\nB'))

Add support for lenient stubbed invocations

Current behaviour: When we call verifyStubbedInvocationsAreUsed it checks that all stubbed invocations are called at least once, ensuring we don't have unnecessary stubbing. This is often useful to have in afterEach hook. However, there are cases where we want some specific mocks to be skipped by this check. Java mockito has lenient which allows a stubbed invocation to be set up without being called. This is useful in a case where we want to always mock some call in the beforeEach regardless of whether it is called in the test body (see example below). Currently, there isn't support for this.

class TestCase:
    def setUp(self):
        mockito.when("external_module").real_call_that_should_always_be_stubbed(...).thenReturn("Some response")

    def tearDown(self):
        mockito.verifyStubbedInvocationsAreUsed()

    def test_some_behaviour(self):
        # Do some stuff that don't call the stub

    def test_some_other_behaviour(self):
        # Do some stuff that call the stub

Proposed Behaviour: Thinking this can be solved in one of two ways:

  1. Have an optional lenient argument added to when, when2, expect, patch. The actual name of the argument can be something other than lenient as we currently have strict and this can be confusing. Also Java Mockito's lenient argument seems to skip argument mismatch check so having the same name (but modified behaviour) could be misleading.
  2. Relax the validity check on the atleast argument in expect so zero can be passed as a valid argument. Hence in beforeEach, user can specify mockito.expect("external_module", atleast=0).real_call_that_should_always_be_stubbed(...).thenReturn("Some response"). And this will still pass verifyStubbedInvocationsAreUsed in afterEach hook even if no calls are made to stubbed invocation in the test body.

I think Option 2 will be the least invasive but I'm keen to hear others thoughts.

I will also be happy to raise a PR with the changes required

Moking property with execption

I would like to mock and object attribute and when the attribute is called I would like to raise an exception. I would like to do something like this:

def test(self):
    element = mock()
    when(element).text.thenRaise(ValueError('foo'))

But because when can only mock method, the above does not quite work. I also tried several other ways, but could not get it working. Is there a way to make the mock attribute to raise and exception?

Mocking inherited methods from super class

I currently get weird error messages while testing my business logic in my controller classes.

My project/controller looks like this:

class ControllerBase:
    pass

class RequestControllerBase(ControllerBase):
    # I already have tests for this... 
    def _request_data_validation(...):
        """ request data validation. Returns request data """

class UserSessionRequestControllerBase(RequestControllerBase):
    # I already have tests for this... 
    def _user_session_validation(...):
        """ user session validation. Returns of user id """

class SpecificControllerImplemention(UserSessionRequestControllerBase):
    # I want to write tests for this and mock out the existing methods in super classes
    def business_login(request: Request):
        user_id = self._user_session_validation(request)
        request_data = self._request_data_validation(request)
        # now the business logic follows
        # ....

When trying to mock self._request_data_validation(request) with when() I get the following error:

E           You called
E           
E                   _validate_request_data(request=<DummyRequest id=4402099424>, validation_schema=<UpdateDataSchema(many=False, strict=False)>),
E           
E           which is not expected. Stubbed invocations are:
E           
E                   _validate_request_data(request=<DummyRequest id=4402099424>, validation_schema=<UpdateDataSchema(many=False, strict=False)>)
E      

What am I doing wrong? Or is it an issue on the side of mockito's when fixture?
I am using Python 3.6 and pytest.

Thanks it advance!

How to verify stubbed methods (from different mocks) are called in order?

Hello,

I didnโ€™t find this information in the documentation, so how would I achieve that?

E.g. Makes sure that this passes:

verify(self._subscriber1, times=1).run('data')
verify(self._subscriber2, times=1).run('data')
verify(self._subscriber3, times=1).run('data')

but this fails

verify(self._subscriber3, times=1).run('data')
verify(self._subscriber2, times=1).run('data')
verify(self._subscriber1, times=1).run('data')

Thank you

Backwards incompatable change between version 0.7.1 and 1.0.0

In version 0.7.1 this used to work:

from mockito import mock

class ClassName(mock):

But in version 1.0.1 it says:

Traceback (most recent call last):
  File "/path/to/test/test_something.py", line 108, in test_create_something
    class ClassName(mock):
TypeError: Error when calling the metaclass bases
    function() argument 1 must be code, not str

Is this expected and I should change how my unit test work, or is this bug in mockito which could be fixed in some time frame?

Error in 1.0.8 and 1.0.9: TypeError: 'staticmethod' object is not callable

When I have this code:

from mockito import mock, when
from appium import webdriver

url = 'localhost'
desired_capabilites = {'key1': 'value1'}
mock_driver = mock()
when(webdriver).Remote(url, desired_capabilites).thenReturn(mock_driver)
driver = webdriver.Remote(url, desired_capabilites)

Then in mockito 1.0.7 it works fine, but in 1.0.8 and 1.0,9 it fails with error:

Traceback (most recent call last):
  File "C:\tmp\foo.py", line 8, in <module>
    driver = webdriver.Remote(url, desired_capabilites)
TypeError: 'staticmethod' object is not callable

If needed appium can be installed with: pip install Appium-Python-Client==0.24

Error while installing mockito==0.5.2

Hi There,
I was trying to install mockito 0.5.2 version with below setup
Centos - 6.6
python - 2.6.6
pip - 1.5.6

But I was getting error messages even though I had nose (1.3.7) is installed on the box.
It would be great help if you can shed some light on this.

$pip install mockito==0.5.2
Downloading/unpacking mockito==0.5.2
  Downloading mockito-0.5.2.tar.gz
  Running setup.py (path:/tmp/pip_build_root/mockito/setup.py) egg_info for package mockito
    Couldn't find index page for 'nose' (maybe misspelled?)
    No local packages or download links found for nose
    Traceback (most recent call last):
      File "<string>", line 17, in <module>
      File "/tmp/pip_build_root/mockito/setup.py", line 36, in <module>
        **extra)
      File "/usr/lib64/python2.6/distutils/core.py", line 113, in setup
        _setup_distribution = dist = klass(attrs)
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 221, in __init__
        self.fetch_build_eggs(attrs.pop('setup_requires'))
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 245, in fetch_build_eggs
        parse_requirements(requires), installer=self.fetch_build_egg
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 538, in resolve
        dist = best[req.key] = env.best_match(req, self, installer)
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 780, in best_match
        return self.obtain(req, installer) # try and download/install
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 792, in obtain
        return installer(requirement)
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 293, in fetch_build_egg
        return cmd.easy_install(req)
      File "/usr/lib/python2.6/site-packages/setuptools/command/easy_install.py", line 466, in easy_install
        raise DistutilsError(msg)
    distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('nose')
    Complete output from command python setup.py egg_info:
    Couldn't find index page for 'nose' (maybe misspelled?)

No local packages or download links found for nose

Traceback (most recent call last):

  File "<string>", line 17, in <module>

  File "/tmp/pip_build_root/mockito/setup.py", line 36, in <module>

    **extra)

  File "/usr/lib64/python2.6/distutils/core.py", line 113, in setup

    _setup_distribution = dist = klass(attrs)

  File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 221, in __init__

    self.fetch_build_eggs(attrs.pop('setup_requires'))

  File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 245, in fetch_build_eggs

    parse_requirements(requires), installer=self.fetch_build_egg

  File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 538, in resolve

    dist = best[req.key] = env.best_match(req, self, installer)

  File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 780, in best_match

    return self.obtain(req, installer) # try and download/install

  File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 792, in obtain

    return installer(requirement)

  File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 293, in fetch_build_egg

    return cmd.easy_install(req)

  File "/usr/lib/python2.6/site-packages/setuptools/command/easy_install.py", line 466, in easy_install

    raise DistutilsError(msg)

distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('nose')

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /tmp/pip_build_root/mockito
Storing debug log for failure in /root/.pip/pip.log

captor is capturing even when the whole signature doesn't match.

Hello again :)

I've detected a bug with captor. It is called in the argument matching phase but if the matching phase fails, the argument is still remembered in the captor.
It is visible when using captors with two different signatures of the same function:

from mockito import when, captor, ANY

class A:
    @staticmethod
    def method(a,b):
        pass

a = captor()
when(A).method(a, ANY(int)).thenReturn()
when(A).method(a, ANY(str)).thenReturn()

A.method(10, 20)

print(a.all_values)
# [10, 10]

ArgumentError: obj is not registered

My test fails with an unexpected error. As far as I can tell from the documentation, I'm doing this right. Am I missing something?

from mockito import mock, verify

mock_feature_0 = mock()
def test_something():
    verify(mock_feature_0).listen(...)

This test should fail because listen was not invoked. Instead I get this error.

mockito.mockito.ArgumentError: obj '<Dummy id=139889037572992>' is not registered

It seems that the error comes from _get_mock_or_raise in mockito.py. I don't know how to register my mock other than what I'm already doing.

Other Information

$ python3 -m pytest --version
pytest 7.1.2

Test command

python3 -m pytest -v

mock function docstring typo

mock function docstring typo:

when(dummy).__call_(1).thenReturn(2)

missing underscore in method call i.e.

__call_ should be __call__

Caught me out! Still, I learned about call in the process :)

getting the valid input in mock

Hi,

I tried to mock the input type of a function create_service in services.py .

from mockito import verify, when, ANY, not_
from .api import services
from .models import Token


class TestCreateServicePrincipal(TestCase):
	def test_input_name_service_principal(self):
		when(services).create_service(ANY(Token), ANY(str), None, None, "p",False).thenReturn(1)
		when(services).create_service(not_(ANY(Token)), ANY(str), None, None, "p",False).thenRaise(TypeError)
		when(services).create_service(ANY(Token), not_( ANY(str)), None, None, "p",False).thenRaise(TypeError)
		when(services).create_service(not_(ANY(Token)), not_( ANY(str)), None, None, "p",False).thenRaise(TypeError)
		verify(services).create_service(ANY(Token), ANY(str), None, None, "p",False)
                #verify(services).create_service(not_(ANY(Token)), ANY(str), None, None, "p",False)
                #verify(services).create_service(ANY(Token), not_( ANY(str)), None, None, "p",False).thenRaise(TypeError)
		#verify(services).create_service(not_(ANY(Token)), not_( ANY(str)), None, None, "p",False).thenRaise(TypeError)

I am setting up a mock with when and then using verify to evaluate the valid or invalid inputs but I got the following error. Is there any way I can fix the error?

mockito.verification.VerificationError: Wanted but not invoked: create_service(<Any: <class 'Token'>>, <Any: <class 'str'>>, None, None, 'p', False) Instead got: Nothing

Add method to clear all invocations for a mock

Sometimes setting up test environment is hard and might involve some invocations on a mock object. But at the end we want to check only invocations that were made after particular action without need to take into account previous invocations. It would be nice to have a method to clear all invocations once setup is done so that test can check only invocations that happened afterwards.

Example:

import mockito

mock_obj = mockito.mock( ... )
obj_under_test = MyObject(mock_obj)   # this makes some invocations on mock_obj

mockito.clear(mock_obj)

obj_under_test.do_stuff()  # triggers more invocations on mock_obj that we want to test

mockito.verify(mock_obj).do_foo()

This feature was merged into Java Mockito.

Mockito does not unstub inherited methods

Hello! The following looks like a bug to me, but if it is not โ€” please advice how to circumvent this behaviour.

Steps to reproduce: run the following code

import mockito

class A:
    @staticmethod
    def foo():
        return "result"

class B(A):
    pass

B = mockito.mock()
mockito.unstub()

print(B.foo())

Expected behaviour: "result" is printed.
Actual behaviour in mockito-1.3.3 installed from PyPI on Python 3.9: "None" is printed.

I expect that when I first mocked B and then unstubbed all mocks, B.foo() should execute the inherited method A.foo() and return me the string "result". What happens is that B.foo() returns the Dummy object. Which seems strange, because the documentation for unstub() promises to remove all mocks

I believe I saw a similar bug report somewhere in 2016, but I did not manage to find it again and attach it here.

Thanks in advance for any advice!

No longer working with Python 2.7

For a legacy project I still need to use Python 2.7. I recently upgraded to the latest version of mockito, but then started getting errors:

ImportError: Failed to import test module: test_web_hooks_helper
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/unittest/loader.py", line 254, in _find_tests
    module = self._get_module_from_name(name)
  File "/usr/local/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
    __import__(name)
  File "/my-file.py", line 6, in <module>
    from mockito import *
  File "/usr/local/lib/python2.7/site-packages/mockito/__init__.py", line 24, in <module>
    from .mockito import (
  File "/usr/local/lib/python2.7/site-packages/mockito/mockito.py", line 23, in <module>
    from . import invocation
  File "/usr/local/lib/python2.7/site-packages/mockito/invocation.py", line 21, in <module>
    from . import matchers
  File "/usr/local/lib/python2.7/site-packages/mockito/matchers.py", line 62, in <module>
    from abc import ABC, abstractmethod
ImportError: cannot import name ABC

This is the offending line:

from abc import ABC, abstractmethod

The cause of the failure is the different way that abc is used. See https://docs.python.org/2.7/library/abc.html#module-abc for more information.

I see two different ways to handle this:

  1. Find some way to use abc and abstractmethod for both Python 2.7 and 3.x
  2. Simply drop support for Python 2.7

As for me, my workaround was to use version 1.3.0, which is the last version to not use the incompatible import.

Getting the mocked obj instance in the mock method

Hello ! First, I really like mockito-python โค๏ธ

Now my problem :)
If I want to mock the method of a class with thenAnswer and then have access to the instance self inside the stub function. I've tried different ways but I don't find any way to do so:

from mockito import when
class A:
    def __init__(self, a):
        self.a = a
   
    def method(self):
        print(self.a)

def fake_method(self_A):
    print("fake:", self_A.a)

when(A).method().thenAnswer(fake_method)
A(10).method()
# > TypeError: fake_method() missing 1 required positional argument: 'self_A'

With pytest.monkeypatch or pytest-mock , I usualy use functools.partialmethod to do that, but mockito tries to call directly the partialmethod and fails.

I've looked inside and the culprit seems to be the throw-away of self/cls in new_mocked_method so I think there is no way to get self in the mock.

Thank for the help. I'm open to do a PR but I failed to make it works easily....

Issue unstubbing random.randint

When trying to monkeypatch random.randint and then later unstubbing the original method does not get reinstated. Example:

import sys
sys.version
Out[3]: '3.10.4 (main, Mar 31 2022, 03:38:35) [Clang 12.0.0 ]'
import random
import mockito
mockito.__version__
Out[6]: '1.2.2'
random.randint(1, 10)
Out[7]: 6
mockito.when(random).randint(...).thenReturn("Mocked!")
Out[8]: <mockito.invocation.AnswerSelector at 0x7ff076012c80>
random.randint(1, 10)
Out[9]: 'Mocked!'
mockito.unstub()
random.randint(1, 10)
Traceback (most recent call last):
...
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-11-a5705e42ffa6>", line 1, in <cell line: 1>
    random.randint(1, 10)
AttributeError: module 'random' has no attribute 'randint'`

This seems to be because:

not inspect.isclass(self.mocked_obj) and inspect.ismethod(original_method)

in the method restore_method within mocking returns True when it should be False.

Test failures with Python 3.8

=================================== FAILURES ===================================
_________________ TestFancyObjResolver.testWhenSplitOnNextLine _________________

self = <mockito.tests.when2_test.TestFancyObjResolver object at 0x7f45e8ee8ac0>

    def testWhenSplitOnNextLine(self):
        # fmt: off
>       when2(
            os.path.commonprefix, '/Foo').thenReturn(True)

build/lib/mockito/tests/when2_test.py:77: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
build/lib/mockito/mockito.py:243: in when2
    obj, name = get_obj_attr_tuple(fn)
build/lib/mockito/utils.py:159: in get_obj_attr_tuple
    return get_function_host(path)
build/lib/mockito/utils.py:59: in get_function_host
    obj, name = find_invoking_frame_and_try_parse()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def find_invoking_frame_and_try_parse():
        # Actually we just want the first frame in user land; we're open for
        # refactorings here and don't yet decide on which frame exactly we hit
        # that user land.
        stack = inspect.stack(2)[2:10]
        for frame_info in stack:
            # Within `patch` and `spy2` we delegate to `when2` but that's not
            # user land code
            if frame_info[3] in ('patch', 'spy2'):
                continue
    
            source = ''.join(frame_info[4])
            m = FIND_ID.match(source)
            if m:
                # id should be something like `os.path.exists` etc.
                id = m.group(1)
                parts = id.split('.')
                if len(parts) < 2:
                    raise TypeError("can't guess origin of '%s'" % id)
    
                frame = frame_info[0]
                vars = frame.f_globals.copy()
                vars.update(frame.f_locals)
    
                # Now that's a simple reduce; we get the initial value from the
                # locally available `vars`, and then reduce the middle parts via
                # `getattr`. The last path component gets not resolved, but is
                # returned as plain string value.
                obj = vars.get(parts[0])
                for part in parts[1:-1]:
                    obj = getattr(obj, part)
                return obj, parts[-1]
    
>       raise TypeError('could not destructure first argument')
E       TypeError: could not destructure first argument

build/lib/mockito/utils.py:103: TypeError
______________ TestFancyObjResolver.testEnsureWithWhen2SplitLine _______________

self = <mockito.tests.when2_test.TestFancyObjResolver object at 0x7f45e8fc6160>

    def testEnsureWithWhen2SplitLine(self):
        # fmt: off
>       with when2(
                os.path.commonprefix, '/Foo'):

build/lib/mockito/tests/when2_test.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
build/lib/mockito/mockito.py:243: in when2
    obj, name = get_obj_attr_tuple(fn)
build/lib/mockito/utils.py:159: in get_obj_attr_tuple
    return get_function_host(path)
build/lib/mockito/utils.py:59: in get_function_host
    obj, name = find_invoking_frame_and_try_parse()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def find_invoking_frame_and_try_parse():
        # Actually we just want the first frame in user land; we're open for
        # refactorings here and don't yet decide on which frame exactly we hit
        # that user land.
        stack = inspect.stack(2)[2:10]
        for frame_info in stack:
            # Within `patch` and `spy2` we delegate to `when2` but that's not
            # user land code
            if frame_info[3] in ('patch', 'spy2'):
                continue
    
            source = ''.join(frame_info[4])
            m = FIND_ID.match(source)
            if m:
                # id should be something like `os.path.exists` etc.
                id = m.group(1)
                parts = id.split('.')
                if len(parts) < 2:
                    raise TypeError("can't guess origin of '%s'" % id)
    
                frame = frame_info[0]
                vars = frame.f_globals.copy()
                vars.update(frame.f_locals)
    
                # Now that's a simple reduce; we get the initial value from the
                # locally available `vars`, and then reduce the middle parts via
                # `getattr`. The last path component gets not resolved, but is
                # returned as plain string value.
                obj = vars.get(parts[0])
                for part in parts[1:-1]:
                    obj = getattr(obj, part)
                return obj, parts[-1]
    
>       raise TypeError('could not destructure first argument')
E       TypeError: could not destructure first argument

build/lib/mockito/utils.py:103: TypeError
================== 2 failed, 1125 passed, 1 xfailed in 2.29s ===================

Looks like the frame info doesn't contain needed info anymore with Python 3.8:

FrameInfo(frame=<frame at 0x7f59767aea40, file '/build/python-mockito/src/mockito-python-1.1.1/build/lib/mockito/tests/when2_test.py', line 88, code testEnsureWithWhen2SplitLine>, filename='/build/python-mockito/src/mockito-python-1.1.1/build/lib/mockito/tests/when2_test.py', lineno=88, function='testEnsureWithWhen2SplitLine', code_context=['        # fmt: off\n', '        with when2(\n'], index=1)

Patching built-in/extension types

I am having problems patching io.BytesIO. When doing:

    bytesIO_mock = mock(strict=True)
    when(bytesIO_mock).getvalue().thenReturn('data')
    patch(BytesIO.__new__, lambda: bytesIO_mock)

I am getting the following error:

def set_method(self, method_name, new_method):
>       setattr(self.mocked_obj, method_name, new_method)
E       TypeError: can't set attributes of built-in/extension type '_io.BytesIO'
    
/venv/lib/python3.6/site-packages/mockito/mocking.py:74: TypeError`

Am I doing anything wrong, or is this a current limitation?

The following link contains a more detailed description of the issue:
http://stackoverflow.com/questions/43031393/how-to-patch-built-in-extension-types-in-python-using-mockito

Captor example is not working

I'm trying out captor example given here

This is my code

from mockito import captor
 captor_test = captor(any(int))

This throws an exception when I ran it

Error
Traceback (most recent call last):
  File "C:\Program Files (x86)\Python37-32\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Program Files (x86)\Python37-32\lib\unittest\case.py", line 628, in run
    testMethod()
  File "C:\Program Files (x86)\Python37-32\lib\unittest\mock.py", line 1626, in _inner
    return f(*args, **kw)
  File "<ommitted>", line 45, in <ommitted function name>
    captor_test = captor(any(int))
TypeError: 'type' object is not iterable

Am I missing something or the example is not up to date?

Mocking attributes which initially value None

If I have this code:

from selenium import webdriver

from mockito import mock, when


class ClassName(object):

    driver = None

    def create_driver(self, browser):
        if browser == 'chrome':
            self.driver = webdriver.Chrome()
        else:
            self.driver = webdriver.Firefox()

    def find_element(self, selector):
        element = self.driver.find_element_by_xpath(selector)
        if element:
            print 'Do something complex which I want to test'


x = ClassName()
when(x.driver).find_element_by_xpath('//dvi').thenReturn(mock())

Then I could not figure out an easy way to mock the element = self.driver.find_element_by_xpath(selector) line. I did try several ways with when or other ways to define the driver, but all of them ended with different type of errors. Usually with AttributeError: 'NoneType' object has no attribute 'find_element_by_xpath' How the code should be changed that the mocking would work?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.