I have this class and interfaces:
class Class1(
private val interface1: Interface1,
private val interface2: Interface2,
private val interface3: Interface3,
private val coroutineScope: CoroutineScope
) {
fun start() {
coroutineScope.launch {
val string = interface1.execute { interface2.execute() }
interface3.execute(string)
}
}
}
interface Interface1 {
suspend fun execute(block: suspend () -> String): String
}
interface Interface2 {
suspend fun execute(): String
}
interface Interface3 {
fun execute(string: String)
}
When creating an actual implementation of this all works fine (interface1 is executed, then interface2 is executed, then interface3 is executed):
class MainActivity : AppCompatActivity(), Interface1, Interface2, Interface3 {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val c = Class1(this, this, this, GlobalScope)
c.start()
}
// implements interface1
override suspend fun execute(block: suspend () -> String): String {
Log.d("xyz1", "execute interface1")
return block()
}
// implements interface2
override suspend fun execute(): String {
Log.d("xyz1", "execute interface2")
return "someString"
}
// implements interface3
override fun execute(string: String) {
Log.d("xyz1", "execute interface3 $string")
}
}
Now I try to test Class1 with the following test:
@ExperimentalCoroutinesApi
class Class1Test {
private lateinit var interface1: Interface1
private lateinit var interface2: Interface2
private lateinit var interface3: Interface3
private lateinit var tested: Class1
@Before
fun setUp() {
interface1 = mock(Interface1::class.java)
interface2 = mock(Interface2::class.java)
interface3 = mock(Interface3::class.java)
tested = Class1(interface1, interface2, interface3, TestCoroutineScope())
}
@Test
fun `should execute on interface1 then interface2 and then interface3`() = runBlockingTest {
whenever(interface2.execute()).thenReturn("myString")
tested.start()
val captor = argumentCaptor<suspend () -> String>()
verify(interface1).execute(captor.capture())
captor.firstValue.invoke()
then(interface3).should().execute("myString")
}
}
But interface3.execute() is never called.
Debugging it, it seems like string is either not assigned or assigned too late (the lower breakpoint is reached, the upper one not):
Why is this test failing and how do I fix it?
PS: Changes to the production code are not an option. PPS: If you want to play around with it, my little test project can be found here: https://github.com/fmweigl/SuspendTest
