Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: add a .thenDoNothing() method to AnswerSelector? #69

Open
fge opened this issue Nov 15, 2023 · 8 comments
Open

Feature request: add a .thenDoNothing() method to AnswerSelector? #69

fge opened this issue Nov 15, 2023 · 8 comments

Comments

@fge
Copy link

fge commented Nov 15, 2023

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:

def thenDoNothing(self):
    thenAnswer(lambda: *args = None)
@kaste
Copy link
Owner

kaste commented Nov 15, 2023

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.

@fge
Copy link
Author

fge commented Nov 16, 2023

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 :)

@kaste
Copy link
Owner

kaste commented Nov 16, 2023

Oh, didn't realize that thenReturn() or thenAnswer() without any args also works.

@fge
Copy link
Author

fge commented Nov 16, 2023

Oh, didn't realize that thenReturn() or thenAnswer() 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...

@fge
Copy link
Author

fge commented Nov 17, 2023

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 :)

@fge
Copy link
Author

fge commented Nov 17, 2023

OK, I thought I had closed this issue... Anyway, as said in my previous comment, no need for that after all

@fge fge closed this as completed Nov 17, 2023
@fge
Copy link
Author

fge commented Dec 4, 2023

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? :)

@fge fge reopened this Dec 4, 2023
@kaste
Copy link
Owner

kaste commented Dec 5, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants