Open fge opened 9 months ago
Interesting. thenDoNothing
is just there because it reads nice, I guess. Or is there a specific reason to include it?
Afaik we currently have:
# omit thenReturn and it will always just return None
when(dog).bark(...)
when(dog).bark(...).thenReturn(None) # explicit
Omitting thenReturn
doesn't read nice. I think it is there because we also have expect(dog, times=2).bark(...)
et.al.
But besides that, when(dog).bark(...).thenReturn(None)
would be exactly what thenDoNothing()
does. But maybe I'm reading this wrong since stubbed methods do not call the original implementation, hence "do nothing", by default. What I mean is, when you when(dog).bark(...).thenReturn(2)
calling such a method does nothing but returns 2 immediately.
I was thinking about functions not meaning to return any value :) Java's mockito has that shortcut for functions returning void
although the syntax is not exactly the same (doNothing().when(etc etc)
).
But I didn't know that you could just specify no .thenAnswer()
and it would have the same effect... Nice trick :)
Ultimately that's syntactic sugar, of course, so it's your call :)
Oh, didn't realize that thenReturn()
or thenAnswer()
without any args also works.
Oh, didn't realize that
thenReturn()
orthenAnswer()
without any args also works.
That's not what I meant; I meant to just omit.thenReturn()
/.thenAnswer()
as you wrote -- but it is also written in the docs and I missed this part...
Ohwell, I don't think it would be of any use really, now that I know the trick of not specifying .then*()
.
Also, I'm too much influenced by Java... One thing I am starting to fully realise since I first posted this request is that python has no void
, in addition to not having null
either.
Sorry for the noise :)
OK, I thought I had closed this issue... Anyway, as said in my previous comment, no need for that after all
Well, I am on the Python discord and I've been asked this question about this code (values
is a deque
here -- I use Python 3.11 if that can help):
def test_add_updates_deque(self):
sample = 1.0
mean_value = MeanValue()
values = mock(deque)
when(values).appendleft(sample) # <--- This line
mean_value.values = values
mean_value.add(sample)
verify(values, times = 1).appendleft(sample)
The guy who asked me about this line is pretty experienced in Python (much more so than I am) and he didn't get the hang of what this line did.
The way I explained it to him is as such:
You can read this as when(values).appendleft(sample).thenDoNothing(), which is more explicit; and it does what it reads like; "when you call the appendleft method of values with exactly what sample... Well... is (therefore, object identity; anything else will not match), then do nothing -- but do record, as a side effect, that this method was called with this very argument"
The "which is more explicit" is of course my opinion only, of course; but can we just have that method defined which just returns self
? :)
Well, when(values).appendleft(sample)
doesn't read good and I would probably not write it as well. I think expect(values, times=1).appendleft(sample)
would read nice though. Because here expect
means: be aware and prepared that we call this.
The main problem with thenDoNothing
as you would use it here is the confusion that mocks and stubbed callables do nothing in general. That's their point. You turn function calls into constants basically. So when(values).appendleft(sample).thenReturn(None)
actually reads fine like so: whenever you see a values.appendleft(sample)
just return None
.
thenDoNothing
would make more sense if you just record the interaction with an object, like the spy
which always calls the original implementation/function but also records its usage. E.g. (iirc)
def test_add_updates_deque(self):
sample = 1.0
mean_value = MeanValue()
mean_value.values = spy(deque)
mean_value.add(sample)
verify(mean_value.values, times = 1).appendleft(sample)
This one also reads fine, imo:
def test_add_updates_deque(self):
sample = 1.0
mean_value = MeanValue()
mean_value.values = mock(deque)
with expect(mean_value.values, times=1).appendleft(sample):
mean_value.add(sample)
But since .values
is part of the interface of MeanValue
in your example,
def test_add_updates_deque(self):
sample = 1.0
mean_value = MeanValue()
with expect(mean_value.values, times=1).appendleft(sample):
mean_value.add(sample)
probably works as well. The constructor MeanValue
likely initializes the object at .values
and we just capture the one interaction with it.
The original mockito in Java has that and it's very convenient; can this be implemented in python mockito as well?
I'm a beginner in python but AFAICS this can be implemented as such: