-1

Simplified code: I have a function that expects either a number or None, and returns True if it's None, and False if it's a number, like:

def function(var):
    return var is None

I want to pass a mocked object and test it with the is operator like this:

from unittest.mock import Mock

mock = Mock(return_value=None)
assert function()

This will fail although I mocked the value to be None. According to the documentation, there are many supported magic methods, but none let me implement the behavior for this case.

Changing to assert mock == None would let me use the __eq__ operator, but I don't want to touch the code (and apparently using is is faster than == according to this answer)

ezib
  • 49
  • 7
  • 2
    You cannot change `is` – juanpa.arrivillaga Jan 27 '21 at 15:16
  • How could you say `is` would is be faster if you were trying to implement both? – Sayse Jan 27 '21 at 15:17
  • "Changing to assert mock == None would let me use the __eq__ operator, but I would like to avoid it since apparently using is is faster than == according to this answer." This is not going to be a significant performance issue – juanpa.arrivillaga Jan 27 '21 at 15:17
  • @juanpa.arrivillaga: might not be a performance issue, but there's difference in performance >>> timeit.Timer("None == None").timeit() 0.026401887999782048 >>> timeit.Timer("None is None").timeit() 0.02391680099935911 BTW, these are the average values – ezib Jan 27 '21 at 15:21
  • @juanpa.arrivillaga can you be more specific why I can't override `is`? I'm not convinced. – ezib Jan 27 '21 at 15:22
  • 1
    @ezib because the language provides no hook for `is`, which **always** means identity. – juanpa.arrivillaga Jan 27 '21 at 15:23
  • Honestly, what you are describing doesn't make a lot of sense. What are you trying to test such that your mock object must be *identical* (or even equal to) `None`? – juanpa.arrivillaga Jan 27 '21 at 15:25
  • @ezib See https://docs.python.org/3/reference/datamodel.html. `is` compares `id`, which must be unique, and in CPython is the memory address. (Also, this is probably the underlying reason it is faster -- there's no hook to modify it) – dwhswenson Jan 27 '21 at 15:26
  • If you _could_ override the behaviour of `is`, you would lose the speed benefit anyway. – khelwood Jan 27 '21 at 15:27
  • @dwhswenson thanks! That explains a bit why the operator isn't "overridable". – ezib Jan 27 '21 at 15:28
  • @khelwood the operator would be overriden only for the mocked object in a test case, not used on runtime – ezib Jan 27 '21 at 15:29
  • In general, you probably want to mock a function (and results of that function), not the result object. This works: `import mock; m = mock.Mock(return_value=None); assert m() is None` – dwhswenson Jan 27 '21 at 15:29
  • @juanpa.arrivillaga I want to mock, in a test, a variable that can be either None or a number. In the main code, None has a different treatment than for a number – ezib Jan 27 '21 at 15:30
  • I updated the description a bit – ezib Jan 27 '21 at 15:38
  • @ezib I mean if `is` was subject to overriding, it would be slower, even if you were not overriding it. – khelwood Jan 27 '21 at 16:26

3 Answers3

0

Perhaps using isinstance() instead of is would work for you. Declaring your mock object class as class MockInt(int): will make isinstance() recognizes the variables as int:

class MockInt(int):
      def __init__(self,value): self.value = value

i = MockInt(23)

print( isinstance(i,int) ) # True 
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

TL;DR

don't forget the parentheses when there is a return_value

from unittest.mock import Mock

mock = Mock(return_value=None)
assert function(mock())

More info

If you want to pass a variable that holds None to test function there is no need to use Mock. you can write the below code:

mock = None
assert function(mock)

Mock's best usage is when you want to mock either a method or an object with a more complex structure. so, when you write mock = Mock(return_value=None) you must call mock to return the return_value.

0

As pointed out by many, is is "unoverridable" because it's a fast equality check of ids. That's why there's a performance difference in comparison to __eq__.

More about that in the docs and in the comments of this answer.

ezib
  • 49
  • 7