Mockito, testing, nulls and exceptions

I use nulls explicitly in tests to designate parameters that shouldn’t be used by tested logic. In general, NPEs in tests are exceptions like any other exception - they allow us to find problems quickly, before things go to production. OTOH exceptions in production code are evil as they can break user experience. Treating exceptions in production and exceptions in tests differently is the only sane way to use them effectively, I think.

Mockito relies heavily on reflection so there should be no problem for Mockito to supply Nones instead of nulls (if that’s what you want) . Here’s my quick stab at it:

package mocking

import org.mockito.{Answers, Mockito}
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer

import scala.util.Try

object NonesForNulls {
  trait Behavior {
    def greet(): Unit
  }
  case class Struct(fieldOpt: Option[Integer] = None,
                    none: None.type = None,
                    field: Behavior)

  def main(args: Array[String]): Unit = {
    println("With default settings:")
    val mock1 = Mockito.mock(classOf[Struct])
    println(mock1.fieldOpt)
    println(mock1.none)
    println(mock1.field)
    println("...added Option[_] and None awareness:")
    val mock2 = Mockito.mock(
      classOf[Struct],
      optionAwareAnswer(Answers.RETURNS_DEFAULTS)
    )
    println(mock2.fieldOpt)
    println(mock2.none)
    println(mock2.field)

    println("With smart nulls:")
    val mock3 = Mockito.mock(classOf[Struct], Answers.RETURNS_SMART_NULLS)
    println(mock3.fieldOpt)
    println(mock3.none)
    println(mock3.field)
    Try(mock3.field.greet()).failed.foreach(_.printStackTrace(System.out))
    println("...added Option[_] and None awareness:")
    val mock4 = Mockito.mock(
      classOf[Struct],
      optionAwareAnswer(Answers.RETURNS_SMART_NULLS)
    )
    println(mock4.fieldOpt)
    println(mock4.none)
    println(mock4.field)
    Try(mock4.field.greet()).failed.foreach(_.printStackTrace(System.out))
  }

  // this method is probably wrong
  // I haven't tested Options in nested mocks
  def optionAwareAnswer(delegate: Answer[AnyRef]): Answer[AnyRef] = {
    invocation: InvocationOnMock =>
      val returnType = invocation.getMethod.getReturnType
      if (returnType.isAssignableFrom(None.getClass)) {
        None
      } else {
        delegate.answer(invocation)
      }
  }
}

There’s mockito-scala library so maybe that was already done.