In a previous post I looked at the problems I had been having using Mockito in a Kotlin project and the small helper functions that could make things easier. At the end of that post I said that I didnt have a good solution to the problems of using a captor. I really didn’t want to add another dependency like mockito-kotlin. Well it turns out there is a solution.

Argument captors

The problem is that if a method does not take a nullable parameter then an error is generated when the test is run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
fun draw_a_track_with_captor() {
  // arrange
  val displayTrack = setupDisplayTrackWithTwoPoints()

  // act
  presenter.displayTrack(DISPLAY_TRACK_ID)

  // assert
  val captor: ArgumentCaptor<Position> = ArgumentCaptor.forClass(Position::class.java)
  Mockito.verify(mockview, Mockito.times(1)).resetMapToPosition(captor.capture())
  val capturedPositions: List<Position> = captor.allValues
  assertThat("there should only be one position", capturedPositions.size, `is`(1))
}

When we run the test we get this error

java.lang.IllegalStateException: captor.capture() must not be null

  at com.andrewandderek.trailblazer.unittests.ui.main.DisplayTrackTests.draw_a_track_with_captor(DisplayTrackTests.kt:93)
...

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!

This is because the signature for resetMapToPosition() look like this

fun resetMapToPosition(position: Position)

If the method took a Position? then all would be well, but as captor.capture() returns null the test will not run.

Fixing argument captors

I found this one line function on this very useful page Using this function means that we can overone the captor problem without any additional library.

1
2
3
4
object MockitoHelper {
  // use this in place of captor.capture() if you are trying to capture an argument that is not nullable
  fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
}

The test now look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
fun draw_a_track_with_captor() {
  // arrange
  val displayTrack = setupDisplayTrackWithTwoPoints()

  // act
  presenter.displayTrack(DISPLAY_TRACK_ID)

  // assert
  val captor: ArgumentCaptor<Position> = ArgumentCaptor.forClass(Position::class.java)
  Mockito.verify(mockview, Mockito.times(1)).resetMapToPosition(MockitoHelper.capture(captor))
  val capturedPositions: List<Position> = captor.allValues
  assertThat("there should only be one position", capturedPositions.size, `is`(1))
  assertThat("incorrect value", capturedPositions[0].notes, `is`("POSITION_NOTES_1"))
}