diff --git a/README.md b/README.md
index fedba292..275586d4 100644
--- a/README.md
+++ b/README.md
@@ -249,14 +249,19 @@ Check the [tests](https://github.com/mockito/mockito-scala/blob/master/core/src/
## Default Answers
We defined a new type `org.mockito.DefaultAnswer` which is used to configure the default behaviour of a mock when a non-stubbed invocation
-is made on it, the default behaviour is different to the Java version, instead of returning null for any non-primitive or non-final class,
-mockito-scala will return a "Smart Null", which is basically a mock of the type returned by the called method.
-The main advantage of this is that if the code tries to call any method on this mock, instead of failing with a NPE we will
-throw a different exception with a hint of the non-stubbed method call (including its params) that returned this Smart Null,
-this will make it much easier to find and fix a non-stubbed call
-
-Most of the Answers defined in `org.mockito.Answers` have it's counterpart as a `org.mockito.DefaultAnswer`, and on top of that
-we also provide `org.mockito.ReturnsEmptyValues` which will try its best to return an empty object for well known types,
+is made on it.
+
+The object `org.mockito.DefaultAnswers` contains each one of the provided ones
+
+All the mocks created will use `ReturnsSmartNulls` by default, this is different to the Java version, which returns null for any non-primitive or non-final class.
+
+A "Smart Null", is nothing else than a mock of the type returned by the called method.
+The main advantage of doing that is that if the code tries to call any method on this mock, instead of failing with a NPE the mock will
+throw a different exception with a hint of the non-stubbed method that was called (including its params),
+this should make much easier the task of finding and fixing non-stubbed calls
+
+Most of the Answers defined in `org.mockito.Answers` have it's counterpart in `org.mockito.DefaultAnswers`, and on top of that
+we also provide `ReturnsEmptyValues` which will try its best to return an empty object for well known types,
i.e. `Nil` for `List`, `None` for `Option` etc.
This DefaultAnswer is not part of the default behaviour as we think a SmartNull is better, to explain why, let's imagine we
have the following code.
diff --git a/core/src/main/scala/org/mockito/DefaultAnswer.scala b/core/src/main/scala/org/mockito/DefaultAnswer.scala
index d7f15814..f24f0fb2 100644
--- a/core/src/main/scala/org/mockito/DefaultAnswer.scala
+++ b/core/src/main/scala/org/mockito/DefaultAnswer.scala
@@ -1,10 +1,8 @@
package org.mockito
-import java.lang.reflect.Modifier.{isAbstract, isFinal}
+import java.lang.reflect.Modifier.isAbstract
import org.mockito.exceptions.base.MockitoException
-import org.mockito.exceptions.verification.SmartNullPointerException
-import org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.mockito.Answers._
@@ -27,43 +25,18 @@ trait DefaultAnswer extends Answer[Any] with Function[InvocationOnMock, Option[A
object DefaultAnswer {
implicit val defaultAnswer: DefaultAnswer = ReturnsSmartNulls
-}
-object ReturnsDefaults extends DefaultAnswer {
- override def apply(invocation: InvocationOnMock): Option[Any] = Option(RETURNS_DEFAULTS.answer(invocation))
+ def apply(from: Answer[_]): DefaultAnswer = new DecoratedAnswer(from)
}
-object ReturnsSmartNulls extends DefaultAnswer {
- override def apply(invocation: InvocationOnMock): Option[Any] = Option(RETURNS_DEFAULTS.answer(invocation)).orElse {
- val returnType = invocation.getMethod.getReturnType
-
- if (!returnType.isPrimitive && !isFinal(returnType.getModifiers))
- Some(Mockito.mock(returnType, ThrowsSmartNullPointer(invocation)))
- else
- None
- }
-
- private case class ThrowsSmartNullPointer(unStubbedInvocation: InvocationOnMock) extends Answer[Any] {
-
- override def answer(currentInvocation: InvocationOnMock): Any =
- if (isToStringMethod(currentInvocation.getMethod))
- s"""SmartNull returned by this un-stubbed method call on a mock:
- |${unStubbedInvocation.toString}""".stripMargin
- else
- throw new SmartNullPointerException(
- s"""You have a NullPointerException because this method call was *not* stubbed correctly:
- |[$unStubbedInvocation] on the Mock [${unStubbedInvocation.getMock}]""".stripMargin)
- }
-}
-
-object ReturnsDeepStubs extends DefaultAnswer {
- override def apply(invocation: InvocationOnMock): Option[Any] = Option(RETURNS_DEEP_STUBS.answer(invocation))
-}
-
-object CallsRealMethods extends DefaultAnswer {
- override def apply(invocation: InvocationOnMock): Option[Any] = Option(CALLS_REAL_METHODS.answer(invocation))
+class DecoratedAnswer(from: Answer[_]) extends DefaultAnswer {
+ override def apply(invocation: InvocationOnMock): Option[Any] = Option(from.answer(invocation))
}
+object ReturnsDefaults extends DecoratedAnswer(RETURNS_DEFAULTS)
+object ReturnsDeepStubs extends DecoratedAnswer(RETURNS_DEEP_STUBS)
+object CallsRealMethods extends DecoratedAnswer(CALLS_REAL_METHODS)
+object ReturnsSmartNulls extends DecoratedAnswer(RETURNS_SMART_NULLS)
object ReturnsEmptyValues extends DefaultAnswer {
private val javaEmptyValuesAndPrimitives = new ReturnsMoreEmptyValues
@@ -88,3 +61,14 @@ object ReturnsEmptyValues extends DefaultAnswer {
override def apply(invocation: InvocationOnMock): Option[Any] =
Option(javaEmptyValuesAndPrimitives.answer(invocation)).orElse(emptyValues.get(invocation.getMethod.getReturnType))
}
+
+/**
+ * Simple object to act as an 'enum' of DefaultAnswers
+ */
+object DefaultAnswers {
+ val ReturnsDefaults: DefaultAnswer = org.mockito.ReturnsDefaults
+ val ReturnsDeepStubs: DefaultAnswer = org.mockito.ReturnsDeepStubs
+ val CallsRealMethods: DefaultAnswer = org.mockito.CallsRealMethods
+ val ReturnsSmartNulls: DefaultAnswer = org.mockito.ReturnsSmartNulls
+ val ReturnsEmptyValues: DefaultAnswer = org.mockito.ReturnsEmptyValues
+}
diff --git a/core/src/main/scala/org/mockito/IdiomaticMockito.scala b/core/src/main/scala/org/mockito/IdiomaticMockito.scala
index 864c3d6d..bc5f08f5 100644
--- a/core/src/main/scala/org/mockito/IdiomaticMockito.scala
+++ b/core/src/main/scala/org/mockito/IdiomaticMockito.scala
@@ -13,7 +13,7 @@ trait IdiomaticMockito extends MockCreator {
override def mock[T <: AnyRef: ClassTag: TypeTag](mockSettings: MockSettings): T = MockitoSugar.mock[T](mockSettings)
- override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: Answer[_]): T = MockitoSugar.mock[T](defaultAnswer)
+ override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: DefaultAnswer): T = MockitoSugar.mock[T](defaultAnswer)
override def mock[T <: AnyRef: ClassTag: TypeTag](implicit defaultAnswer: DefaultAnswer): T =
MockitoSugar.mock[T]
diff --git a/core/src/main/scala/org/mockito/MockitoAPI.scala b/core/src/main/scala/org/mockito/MockitoAPI.scala
index d5e5da2d..da7cb8f1 100644
--- a/core/src/main/scala/org/mockito/MockitoAPI.scala
+++ b/core/src/main/scala/org/mockito/MockitoAPI.scala
@@ -27,12 +27,20 @@ import scala.reflect.runtime.universe.TypeTag
private[mockito] trait MockCreator {
def mock[T <: AnyRef: ClassTag: TypeTag](implicit defaultAnswer: DefaultAnswer): T
- def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: Answer[_]): T
+ def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: Answer[_]): T = mock[T](DefaultAnswer(defaultAnswer))
+ def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: DefaultAnswer): T
def mock[T <: AnyRef: ClassTag: TypeTag](mockSettings: MockSettings): T
def mock[T <: AnyRef: ClassTag: TypeTag](name: String)(implicit defaultAnswer: DefaultAnswer): T
def spy[T](realObj: T): T
def spyLambda[T <: AnyRef: ClassTag](realObj: T): T
+
+ /**
+ * Delegates to Mockito.withSettings()
, it's only here to expose the full Mockito API
+ */
+ def withSettings(implicit defaultAnswer: DefaultAnswer): MockSettings =
+ Mockito.withSettings().defaultAnswer(defaultAnswer)
+
}
//noinspection MutatorLikeMethodIsParameterless
@@ -115,8 +123,8 @@ private[mockito] trait MockitoEnhancer extends MockCreator {
* verify(aMock).iHaveSomeDefaultArguments("I'm not gonna pass the second argument", "default value")
* as the value for the second parameter would have been null...
*/
- override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: Answer[_]): T =
- mock(withSettings(defaultAnswer.lift))
+ override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: DefaultAnswer): T =
+ mock(withSettings(defaultAnswer))
/**
* Delegates to Mockito.mock(type: Class[T], mockSettings: MockSettings)
@@ -208,12 +216,6 @@ private[mockito] trait MockitoEnhancer extends MockCreator {
*/
def mockingDetails(toInspect: AnyRef): MockingDetails = Mockito.mockingDetails(toInspect)
- /**
- * Delegates to Mockito.withSettings()
, it's only here to expose the full Mockito API
- */
- def withSettings(implicit defaultAnswer: DefaultAnswer): MockSettings =
- Mockito.withSettings().defaultAnswer(defaultAnswer)
-
/**
* Delegates to Mockito.verifyNoMoreInteractions(Object... mocks)
, but ignores the default stubs that
* deal with default argument values
diff --git a/core/src/main/scala/org/mockito/MockitoScalaSession.scala b/core/src/main/scala/org/mockito/MockitoScalaSession.scala
index 585ce090..c7e39431 100644
--- a/core/src/main/scala/org/mockito/MockitoScalaSession.scala
+++ b/core/src/main/scala/org/mockito/MockitoScalaSession.scala
@@ -1,7 +1,8 @@
package org.mockito
-import org.mockito.MockitoScalaSession.UnexpectedInvocationsMockListener
-import org.mockito.exceptions.misusing.UnexpectedInvocationException
+import org.mockito.MockitoScalaSession.{MockitoScalaSessionListener, UnexpectedInvocations}
+import org.mockito.exceptions.misusing.{UnexpectedInvocationException, UnnecessaryStubbingException}
+import org.mockito.internal.stubbing.StubbedInvocationMatcher
import org.mockito.invocation.{DescribedInvocation, Invocation, Location}
import org.mockito.listeners.MockCreationListener
import org.mockito.mock.MockCreationSettings
@@ -13,7 +14,7 @@ import scala.collection.mutable
import scala.collection.JavaConverters._
class MockitoScalaSession(name: String, strictness: Strictness, logger: MockitoSessionLogger) {
- private val listener = new UnexpectedInvocationsMockListener
+ private val listener = new MockitoScalaSessionListener
private val mockitoSession = Mockito.mockitoSession().name(name).logger(logger).strictness(strictness).startMocking()
Mockito.framework().addListener(listener)
@@ -22,16 +23,17 @@ class MockitoScalaSession(name: String, strictness: Strictness, logger: MockitoS
try {
t.fold {
mockitoSession.finishMocking()
- listener.reportUnStubbedCalls().reportUnexpectedInvocations()
+ listener.reportIssues().foreach(_.report())
} {
case e: NullPointerException =>
mockitoSession.finishMocking(e)
- val unStubbedCalls = listener.reportUnStubbedCalls()
- if (unStubbedCalls.nonEmpty)
- throw new UnexpectedInvocationException(s"""A NullPointerException was thrown, check if maybe related to
- |$unStubbedCalls""".stripMargin,
- e)
- else throw e
+ listener.reportIssues().foreach {
+ case unStubbedCalls: UnexpectedInvocations if unStubbedCalls.nonEmpty =>
+ throw new UnexpectedInvocationException(s"""A NullPointerException was thrown, check if maybe related to
+ |$unStubbedCalls""".stripMargin,
+ e)
+ case _ => throw e
+ }
case other =>
mockitoSession.finishMocking(other)
throw other
@@ -63,7 +65,11 @@ object MockitoScalaSession {
override def getLocation: Location = SyntheticLocation
}
- case class UnexpectedInvocations(invocations: Set[Invocation]) {
+ trait Reporter {
+ def report(): Unit
+ }
+
+ case class UnexpectedInvocations(invocations: Set[Invocation]) extends Reporter {
def nonEmpty: Boolean = invocations.nonEmpty
override def toString: String =
@@ -80,25 +86,59 @@ object MockitoScalaSession {
|Please make sure you aren't missing any stubbing or that your code actually does what you want""".stripMargin
} else "No unexpected invocations found"
- def reportUnexpectedInvocations(): Unit =
- if (nonEmpty) throw new UnexpectedInvocationException(toString)
+ def report(): Unit = if (nonEmpty) throw new UnexpectedInvocationException(toString)
}
- class UnexpectedInvocationsMockListener extends MockCreationListener {
- def reportUnStubbedCalls(): UnexpectedInvocations =
- UnexpectedInvocations(
- mocks
- .map(MockitoSugar.mockingDetails)
- .flatMap(_.getInvocations.asScala)
- .filter(_.stubInfo() == null)
- .filterNot(_.isVerified)
- .filterNot(_.getMethod.getName.contains("$default$"))
- .toSet
+ case class UnusedStubbings(stubbings: Set[StubbedInvocationMatcher]) extends Reporter {
+ def nonEmpty: Boolean = stubbings.nonEmpty
+
+ override def toString: String =
+ if (nonEmpty) {
+ val locations = stubbings.zipWithIndex
+ .map {
+ case (stubbing, idx) => s"${idx + 1}. $stubbing ${stubbing.getLocation}"
+ }
+ .mkString("\n")
+ s"""Unnecessary stubbings detected.
+ |
+ |Clean & maintainable test code requires zero unnecessary code.
+ |Following stubbings are unnecessary (click to navigate to relevant line of code):
+ |$locations
+ |Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.""".stripMargin
+ } else "No unexpected invocations found"
+
+ def report(): Unit = if (nonEmpty) throw new UnnecessaryStubbingException(toString)
+ }
+
+ class MockitoScalaSessionListener extends MockCreationListener {
+ def reportIssues(): Seq[Reporter] = {
+ val mockDetails = mocks.toSet.map(MockitoSugar.mockingDetails)
+
+ val stubbings = mockDetails
+ .flatMap(_.getStubbings.asScala)
+ .collect {
+ case s: StubbedInvocationMatcher => s
+ }
+
+ val invocations = mockDetails.flatMap(_.getInvocations.asScala)
+
+ val unexpectedInvocations = invocations
+ .filterNot(_.isVerified)
+ .filterNot(_.getMethod.getName.contains("$default$"))
+ .filterNot(i => stubbings.exists(_.matches(i)))
+
+ val unusedStubbings = stubbings.filterNot(sm => invocations.exists(sm.matches)).filter(!_.wasUsed())
+
+ Seq(
+ UnexpectedInvocations(unexpectedInvocations),
+ UnusedStubbings(unusedStubbings)
)
+ }
private val mocks = mutable.Set.empty[AnyRef]
- override def onMockCreated(mock: AnyRef, settings: MockCreationSettings[_]): Unit = mocks += mock
+ override def onMockCreated(mock: AnyRef, settings: MockCreationSettings[_]): Unit =
+ if (!settings.isLenient) mocks += mock
}
}
diff --git a/core/src/main/scala/org/mockito/integrations/scalatest/ResetMocksAfterEachTest.scala b/core/src/main/scala/org/mockito/integrations/scalatest/ResetMocksAfterEachTest.scala
index e83a8413..8d6962e0 100644
--- a/core/src/main/scala/org/mockito/integrations/scalatest/ResetMocksAfterEachTest.scala
+++ b/core/src/main/scala/org/mockito/integrations/scalatest/ResetMocksAfterEachTest.scala
@@ -3,7 +3,6 @@ package org.mockito.integrations.scalatest
import java.util.concurrent.ConcurrentHashMap
import org.mockito.{DefaultAnswer, MockCreator, MockitoSugar, MockSettings}
-import org.mockito.stubbing.Answer
import org.scalatest.{Outcome, TestSuite}
import scala.collection.JavaConverters._
@@ -37,7 +36,7 @@ trait ResetMocksAfterEachTest extends TestSuite with MockCreator { self: MockCre
abstract override def mock[T <: AnyRef: ClassTag: TypeTag](implicit defaultAnswer: DefaultAnswer): T =
addMock(super.mock[T])
- abstract override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: Answer[_]): T =
+ abstract override def mock[T <: AnyRef: ClassTag: TypeTag](defaultAnswer: DefaultAnswer): T =
addMock(super.mock[T](defaultAnswer))
abstract override def mock[T <: AnyRef: ClassTag: TypeTag](mockSettings: MockSettings): T =
diff --git a/core/src/main/scala/org/mockito/package.scala b/core/src/main/scala/org/mockito/package.scala
index ba0899dd..c337d97a 100644
--- a/core/src/main/scala/org/mockito/package.scala
+++ b/core/src/main/scala/org/mockito/package.scala
@@ -112,10 +112,4 @@ package object mockito {
i.getArgument[P9](9),
i.getArgument[P10](10)
))
-
- implicit class AnswerOps[T](val a: Answer[T]) extends AnyVal {
- def lift: DefaultAnswer = new DefaultAnswer {
- override def apply(invocation: InvocationOnMock): Option[Any] = Option(a.answer(invocation))
- }
- }
}
diff --git a/core/src/test/scala-2.11/org/mockito/MockitoSugarTest_211.scala b/core/src/test/scala-2.11/org/mockito/MockitoSugarTest_211.scala
index bfd566fe..4182f7c2 100644
--- a/core/src/test/scala-2.11/org/mockito/MockitoSugarTest_211.scala
+++ b/core/src/test/scala-2.11/org/mockito/MockitoSugarTest_211.scala
@@ -17,7 +17,7 @@ class MockitoSugarTest_211 extends WordSpec with MockitoSugar with scalatest.Mat
aMock.traitMethod() shouldBe 69
- verify(aMock).traitMethod(0, null)
+ verify(aMock).traitMethod(0, "")
}
}
}
diff --git a/core/src/test/scala/org/mockito/DefaultAnswerTest.scala b/core/src/test/scala/org/mockito/DefaultAnswerTest.scala
index 39ddaff5..86766b8a 100644
--- a/core/src/test/scala/org/mockito/DefaultAnswerTest.scala
+++ b/core/src/test/scala/org/mockito/DefaultAnswerTest.scala
@@ -95,13 +95,11 @@ class DefaultAnswerTest
smartNull should not be null
- val throwable: SmartNullPointerException = the[SmartNullPointerException] thrownBy {
+ val throwable = the[SmartNullPointerException] thrownBy {
smartNull.callMeMaybe()
}
- throwable.getMessage shouldBe
- s"""You have a NullPointerException because this method call was *not* stubbed correctly:
- |[foo.userClass(42);] on the Mock [$aMock]""".stripMargin
+ throwable.getMessage should include("You have a NullPointerException here:")
}
"return a smart standard monad" in {
@@ -113,9 +111,7 @@ class DefaultAnswerTest
smartNull.isEmpty
}
- throwable.getMessage shouldBe
- s"""You have a NullPointerException because this method call was *not* stubbed correctly:
- |[foo.returnsList();] on the Mock [$aMock]""".stripMargin
+ throwable.getMessage should include("You have a NullPointerException here:")
}
"return a default value for primitives" in {
@@ -138,7 +134,7 @@ class DefaultAnswerTest
"ReturnsEmptyValues" should {
"return a default value for primitives" in {
- val primitives = mock[Primitives](ReturnsEmptyValues)
+ val primitives = mock[Primitives](DefaultAnswers.ReturnsEmptyValues)
primitives.barByte shouldBe 0.toByte
primitives.barBoolean shouldBe false
@@ -151,7 +147,7 @@ class DefaultAnswerTest
}
"return the empty values for known classes" in {
- val aMock = mock[KnownTypes](ReturnsEmptyValues)
+ val aMock = mock[KnownTypes](DefaultAnswers.ReturnsEmptyValues)
aMock.returnsOption shouldBe None
aMock.returnsList shouldBe List.empty
diff --git a/core/src/test/scala/org/mockito/MockitoScalaSessionTest.scala b/core/src/test/scala/org/mockito/MockitoScalaSessionTest.scala
index dda6abc5..c6f7fdb0 100644
--- a/core/src/test/scala/org/mockito/MockitoScalaSessionTest.scala
+++ b/core/src/test/scala/org/mockito/MockitoScalaSessionTest.scala
@@ -3,10 +3,10 @@ package org.mockito
import org.scalatest
import org.mockito.exceptions.misusing.{PotentialStubbingProblem, UnexpectedInvocationException, UnnecessaryStubbingException}
import org.mockito.exceptions.verification.SmartNullPointerException
-import org.scalatest.WordSpec
+import org.scalatest.{OptionValues, WordSpec}
//noinspection RedundantDefaultArgument
-class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalatest.Matchers {
+class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalatest.Matchers with OptionValues {
class Foo {
def bar(a: String) = "bar"
@@ -19,11 +19,17 @@ class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalat
}
class Bar {
- def callMeMaybe: Option[Boolean] = None
+ def callMeMaybe: Baz = ???
+ def dontCallMe: Baz = ???
+ }
+
+ class Baz {
+ def callMe: Option[String] = ???
+ def dontCallMe: Option[String] = ???
}
final class BarFinal {
- def callMeMaybe: Option[Boolean] = None
+ def callMeMaybe: Option[Boolean] = ???
}
"MockitoScalaSession" should {
@@ -62,7 +68,7 @@ class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalat
}
}
- thrown.getMessage should startWith("A NullPointerException was thrown, check if maybe related to")
+ thrown.getMessage should startWith("Unexpected invocations found")
}
"check SmartNull" in {
@@ -74,7 +80,7 @@ class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalat
}
}
- thrown.getMessage should startWith("You have a NullPointerException because this method call was *not* stubbed correctly")
+ thrown.getMessage should include("You have a NullPointerException here:")
}
"check incorrect stubs after the expected one was called on a final class" in {
@@ -168,6 +174,67 @@ class MockitoScalaSessionTest extends WordSpec with IdiomaticMockito with scalat
}
}
}
+
+ "don't check unused stubs for lenient mocks" in {
+ MockitoScalaSession().run {
+ val foo = mock[Foo](withSettings.lenient())
+
+ foo.bar("pepe") shouldReturn "mocked"
+
+ foo.bar("pepe")
+
+ foo.bar("paco")
+ }
+ }
+
+ "work with nested deep stubs" in {
+ MockitoScalaSession().run {
+ val foo = mock[Foo](ReturnsDeepStubs)
+
+ foo.userClass.callMeMaybe.callMe shouldReturn Some("my number")
+
+ foo.userClass.callMeMaybe.callMe.value shouldBe "my number"
+ }
+ }
+
+ "not fail if a final deep stub is called in a non stubbed method" in {
+ MockitoScalaSession().run {
+ val foo = mock[Foo](ReturnsDeepStubs)
+
+ foo.userClass.callMeMaybe.callMe shouldReturn Some("my number")
+
+ foo.userClass.callMeMaybe.callMe.value shouldBe "my number"
+
+ foo.userClass.callMeMaybe.dontCallMe
+
+ }
+ }
+
+ "not fail if a nested deep stub is called in a non stubbed method" in {
+ MockitoScalaSession().run {
+ val foo = mock[Foo](ReturnsDeepStubs)
+
+ foo.userClass.callMeMaybe.callMe shouldReturn Some("my number")
+
+ foo.userClass.callMeMaybe.callMe.value shouldBe "my number"
+
+ foo.userClass.dontCallMe
+
+ }
+ }
+
+ "fail if a nested deep stub is stubbed but not used" in {
+ val thrown = the[UnnecessaryStubbingException] thrownBy {
+ MockitoScalaSession().run {
+ val foo = mock[Foo](ReturnsDeepStubs)
+
+ foo.userClass.callMeMaybe.callMe shouldReturn Some("my number")
+
+ }
+ }
+
+ thrown.getMessage should startWith("Unnecessary stubbings detected")
+ }
}
}
diff --git a/core/src/test/scala/org/mockito/MockitoSugarTest.scala b/core/src/test/scala/org/mockito/MockitoSugarTest.scala
index 99e5c102..373da005 100644
--- a/core/src/test/scala/org/mockito/MockitoSugarTest.scala
+++ b/core/src/test/scala/org/mockito/MockitoSugarTest.scala
@@ -107,7 +107,7 @@ class MockitoSugarTest extends WordSpec with MockitoSugar with scalatest.Matcher
when(aMock.iStartWithByNameArgs("arg1", "arg2")) thenReturn "mocked!"
aMock.iStartWithByNameArgs("arg1", "arg2") shouldBe "mocked!"
- aMock.iStartWithByNameArgs("arg111", "arg2") shouldBe null
+ aMock.iStartWithByNameArgs("arg111", "arg2") shouldBe ""
verify(aMock).iStartWithByNameArgs("arg1", "arg2")
verify(aMock).iStartWithByNameArgs("arg111", "arg2")
@@ -119,7 +119,7 @@ class MockitoSugarTest extends WordSpec with MockitoSugar with scalatest.Matcher
when(aMock.iHavePrimitiveByNameArgs(1, "arg2")) thenReturn "mocked!"
aMock.iHavePrimitiveByNameArgs(1, "arg2") shouldBe "mocked!"
- aMock.iHavePrimitiveByNameArgs(2, "arg2") shouldBe null
+ aMock.iHavePrimitiveByNameArgs(2, "arg2") shouldBe ""
verify(aMock).iHavePrimitiveByNameArgs(1, "arg2")
verify(aMock).iHavePrimitiveByNameArgs(2, "arg2")
@@ -131,7 +131,7 @@ class MockitoSugarTest extends WordSpec with MockitoSugar with scalatest.Matcher
when(aMock.iHaveFunction0Args(eqTo("arg1"), function0("arg2"))) thenReturn "mocked!"
aMock.iHaveFunction0Args("arg1", () => "arg2") shouldBe "mocked!"
- aMock.iHaveFunction0Args("arg1", () => "arg3") shouldBe null
+ aMock.iHaveFunction0Args("arg1", () => "arg3") shouldBe ""
verify(aMock).iHaveFunction0Args(eqTo("arg1"), function0("arg2"))
verify(aMock).iHaveFunction0Args(eqTo("arg1"), function0("arg3"))
@@ -156,8 +156,8 @@ class MockitoSugarTest extends WordSpec with MockitoSugar with scalatest.Matcher
reset(aMock)
- aMock.bar shouldBe null
- aMock.iHavePrimitiveByNameArgs(1, "arg2") shouldBe null
+ aMock.bar shouldBe ""
+ aMock.iHavePrimitiveByNameArgs(1, "arg2") shouldBe ""
//to verify the reset mock handler still handles by-name params
when(aMock.iHavePrimitiveByNameArgs(1, "arg2")) thenReturn "mocked!"