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

Multiple ...Many expectations behave counter-intuitively #77

Open
TWiStErRob opened this issue Dec 23, 2023 · 2 comments
Open

Multiple ...Many expectations behave counter-intuitively #77

TWiStErRob opened this issue Dec 23, 2023 · 2 comments
Assignees
Labels

Comments

@TWiStErRob
Copy link

interface I {
	fun f(): Int
}

class ITest {
	@Mock val mockI: I = mock(classOf<I>())

	@Test fun f() {
		every { mockI.f() }.returnsMany(1, 2)
		every { mockI.f() }.returnsMany(3, 4)

		val expected = listOf(1, 2, 3, 4)
		val actual = listOf(mockI.f(), mockI.f(), mockI.f(), mockI.f())
		assertEquals(expected, actual)

		verify { mockI.f() }.wasInvoked(exactly = 4.times)
		verifyNoUnverifiedExpectations(mockI)
		verifyNoUnmetExpectations(mockI)
	}
}
Background context

I originally wanted to test what happens if one of the mapping inside a loop was failing. I picked the one to fail to be the first one, and continued to set up the mocks like this:

every { mapper.map(any()) }.throws(ex)
every { mapper.map(any()) }.returnsMany(obj1, obj2)

this didn't work, because throws will just throw every time. Or at least that's what I thought (the error was too long so I didn't read it :).

So I went on to check the code and based on ClosedBlockingStub's behavior, I set it up like this:

every { mapper.map(any()) }.throwsMany(ex)
every { mapper.map(any()) }.returnsMany(obj1, obj2)

It still didn't work, so I read the error message, and to all my confusion the solution was to swap them:

every { mapper.map(any()) }.returnsMany(obj1, obj2)
every { mapper.map(any()) }.throwsMany(ex)

Made the first iteration of the mapping loop throw, and 2nd and 3rd return the objects respectively. To simplify the repro I used returnsMany twice. Behavior is the same.

@TWiStErRob
Copy link
Author

TWiStErRob commented Dec 23, 2023

The problem is Mockable.addBlockingStub prepends, why?

I guess the answer is to support:

every { mockI.f() }.returns(1) // ignored
every { mockI.f() }.returns(2)

assertEquals(2, mockI.f())

which makes sense, but for mixed ...Many this does not work the same way.

@TWiStErRob
Copy link
Author

Workaround:
instead of

every { mapper.map(any()) }.returnsMany(obj1, obj2)
every { mapper.map(any()) }.throwsMany(ex)

use:

every { mapper.map(any()) }.invokesMany({ throw ex }, { obj1 }, { obj2 })

to make the code-order match the runtime-order.

@Nillerr Nillerr self-assigned this Apr 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants