Skip to content

Add support for stubbing checked exceptions #96

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

Merged
merged 1 commit into from
Apr 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ The library has independent developers, release cycle and versioning from core m

## Dependency

* Artifact identifier: "org.mockito:mockito-scala_<scala-version>:<version>"
* Artifact identifier: "org.mockito:mockito-scala-scalatest_<scala-version>:<version>"
* Artifact identifier: "org.mockito:mockito-scala-specs2_<scala-version>:<version>"
* Artifact identifier: "org.mockito:mockito-scala_[scala-version]:[version]"
* Artifact identifier: "org.mockito:mockito-scala-scalatest_[scala-version]:[version]"
* Artifact identifier: "org.mockito:mockito-scala-specs2_[scala-version]:[version]"
* Latest version - see [release notes](/docs/release-notes.md)
* Repositories: [Maven Central](https://search.maven.org/search?q=mockito-scala) or [JFrog's Bintray](https://bintray.com/mockito/maven/mockito-scala)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.mockito.internal.stubbing.answers
import org.mockito.internal.exceptions.Reporter.cannotStubWithNullThrowable
import org.mockito.invocation.InvocationOnMock

class ScalaThrowsException(t: Throwable) extends ThrowsException(t) {
override def validateFor(invocation: InvocationOnMock): Unit = if (t == null) throw cannotStubWithNullThrowable
}
78 changes: 78 additions & 0 deletions common/src/main/scala/org/mockito/stubbing/ScalaBaseStubbing.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.mockito.stubbing

import org.mockito.internal.ValueClassExtractor
import org.mockito.internal.stubbing.answers.ScalaThrowsException
import org.mockito.invocation.InvocationOnMock
import org.mockito.{ clazz, functionToAnswer, invocationToAnswer }
import org.objenesis.ObjenesisStd

import scala.language.implicitConversions
import scala.reflect.ClassTag

trait ScalaBaseStubbing[T] {

protected def delegate: OngoingStubbing[T]
protected implicit def $vce: ValueClassExtractor[T]

protected def _thenReturn(value: T, values: Seq[T]): ScalaOngoingStubbing[T] =
delegate.thenReturn($vce.extractAs[T](value), values.map($vce.extractAs[T]): _*)

private def thenThrow(t: Throwable): ScalaOngoingStubbing[T] = delegate thenAnswer new ScalaThrowsException(t)

protected def _thenThrow(throwables: Seq[Throwable]): ScalaOngoingStubbing[T] =
if (throwables == null || throwables.isEmpty) thenThrow(null)
else
throwables.tail.foldLeft(thenThrow(throwables.head)) {
case (os, t) => os andThenThrow t
}

protected def _thenThrow[E <: Throwable: ClassTag]: ScalaOngoingStubbing[T] = thenThrow((new ObjenesisStd).newInstance(clazz))

protected def _thenCallRealMethod(): ScalaOngoingStubbing[T] = delegate.thenCallRealMethod()

protected def _thenAnswer(f: => T): ScalaOngoingStubbing[T] = delegate thenAnswer invocationToAnswer(_ => f)
protected def _thenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = clazz[P0] match {
case c if c == classOf[InvocationOnMock] => delegate thenAnswer invocationToAnswer(i => f(i.asInstanceOf[P0]))
case _ => delegate thenAnswer functionToAnswer(f)
}
protected def _thenAnswer[P0, P1](f: (P0, P1) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2](f: (P0, P1, P2) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3](f: (P0, P1, P2, P3) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4](f: (P0, P1, P2, P3, P4) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5](f: (P0, P1, P2, P3, P4, P5) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5, P6](f: (P0, P1, P2, P3, P4, P5, P6) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9](
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
protected def _thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10](
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)

/**
* Returns the mock that was used for this stub.
* <p>
* It allows to create a stub in one line of code.
* This can be helpful to keep test code clean.
* For example, some boring stub can be created & stubbed at field initialization in a test:
* <pre class="code"><code class="java">
* public class CarTest {
* Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();
*
* &#064;Test public void should... {}
* </code></pre>
*
* @param <M> The mock type given by the variable type.
* @return Mock used in this ongoing stubbing.
*/
def getMock[M]: M = delegate.getMock[M]
}
67 changes: 18 additions & 49 deletions common/src/main/scala/org/mockito/stubbing/ScalaFirstStubbing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.mockito.internal.stubbing.OngoingStubbingImpl
import org.mockito.internal.util.MockUtil.getMockSettings
import org.mockito.invocation.InvocationOnMock
import org.mockito.quality.Strictness.LENIENT
import org.mockito.{ clazz, functionToAnswer, invocationToAnswer }

import scala.language.implicitConversions
import scala.reflect.ClassTag
Expand All @@ -15,7 +14,8 @@ object ScalaFirstStubbing {
implicit def toMock[T](s: ScalaFirstStubbing[_]): T = s.getMock[T]
}

case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: ValueClassExtractor[T]) {
case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit protected val $vce: ValueClassExtractor[T])
extends ScalaBaseStubbing[T] {

//noinspection AccessorLikeMethodIsUnit
def isLenient(): Unit = {
Expand All @@ -42,8 +42,7 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: Va
* @param values next return values
* @return object that allows stubbing consecutive calls
*/
def thenReturn(value: T, values: T*): ScalaOngoingStubbing[T] =
delegate.thenReturn($vce.extractAs[T](value), values.map($vce.extractAs[T]): _*)
def thenReturn(value: T, values: T*): ScalaOngoingStubbing[T] = _thenReturn(value, values)

/**
* Sets one or more Throwable objects to be thrown when the method is called. E.g:
Expand All @@ -65,7 +64,7 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: Va
* @param throwables to be thrown on method invocation
* @return object that allows stubbing consecutive calls
*/
def thenThrow(throwables: Throwable*): ScalaOngoingStubbing[T] = delegate thenThrow (throwables: _*)
def thenThrow(throwables: Throwable*): ScalaOngoingStubbing[T] = _thenThrow(throwables)

/**
* Sets a Throwable type to be thrown when the method is called. E.g:
Expand All @@ -88,7 +87,7 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: Va
* @param throwableType to be thrown on method invocation
* @return object that allows stubbing consecutive calls
*/
def thenThrow[E <: Throwable: ClassTag]: ScalaOngoingStubbing[T] = delegate thenThrow clazz
def thenThrow[E <: Throwable: ClassTag]: ScalaOngoingStubbing[T] = _thenThrow

/**
* Sets the real implementation to be called when the method is called on a mock object.
Expand Down Expand Up @@ -119,50 +118,20 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: Va
*
* @return object that allows stubbing consecutive calls
*/
def thenCallRealMethod(): ScalaOngoingStubbing[T] = delegate.thenCallRealMethod()
def thenCallRealMethod(): ScalaOngoingStubbing[T] = _thenCallRealMethod()

def thenAnswer(f: => T): ScalaOngoingStubbing[T] = delegate thenAnswer invocationToAnswer(_ => f)
def thenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = clazz[P0] match {
case c if c == classOf[InvocationOnMock] => delegate thenAnswer invocationToAnswer(i => f(i.asInstanceOf[P0]))
case _ => delegate thenAnswer functionToAnswer(f)
}
def thenAnswer[P0, P1](f: (P0, P1) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2](f: (P0, P1, P2) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3](f: (P0, P1, P2, P3) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4](f: (P0, P1, P2, P3, P4) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5](f: (P0, P1, P2, P3, P4, P5) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6](f: (P0, P1, P2, P3, P4, P5, P6) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def thenAnswer(f: => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1](f: (P0, P1) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2](f: (P0, P1, P2) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3](f: (P0, P1, P2, P3) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4](f: (P0, P1, P2, P3, P4) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5](f: (P0, P1, P2, P3, P4, P5) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6](f: (P0, P1, P2, P3, P4, P5, P6) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
_thenAnswer(f)
def thenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10](
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)

/**
* Returns the mock that was used for this stub.
* <p>
* It allows to create a stub in one line of code.
* This can be helpful to keep test code clean.
* For example, some boring stub can be created & stubbed at field initialization in a test:
* <pre class="code"><code class="java">
* public class CarTest {
* Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();
*
* &#064;Test public void should... {}
* </code></pre>
*
* @param <M> The mock type given by the variable type.
* @return Mock used in this ongoing stubbing.
*/
def getMock[M]: M = delegate.getMock[M]
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.mockito.stubbing

import org.mockito._
import org.mockito.internal.ValueClassExtractor
import org.mockito.invocation.InvocationOnMock

import scala.language.implicitConversions
import scala.reflect.ClassTag
Expand All @@ -12,7 +10,8 @@ object ScalaOngoingStubbing {
implicit def toMock[T](s: ScalaOngoingStubbing[_]): T = s.getMock[T]
}

case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: ValueClassExtractor[T]) {
case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit protected val $vce: ValueClassExtractor[T])
extends ScalaBaseStubbing[T] {

/**
* Sets consecutive return values to be returned when the method is called. E.g:
Expand All @@ -28,8 +27,7 @@ case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce:
* @param values next return values
* @return object that allows stubbing consecutive calls
*/
def andThen(value: T, values: T*): ScalaOngoingStubbing[T] =
delegate.thenReturn($vce.extractAs[T](value), values.map($vce.extractAs[T]): _*)
def andThen(value: T, values: T*): ScalaOngoingStubbing[T] = _thenReturn(value, values)

/**
* Sets Throwable objects to be thrown when the method is called. E.g:
Expand All @@ -50,7 +48,7 @@ case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce:
* @param throwables to be thrown on method invocation
* @return object that allows stubbing consecutive calls
*/
def andThenThrow(throwables: Throwable*): ScalaOngoingStubbing[T] = delegate thenThrow (throwables: _*)
def andThenThrow(throwables: Throwable*): ScalaOngoingStubbing[T] = _thenThrow(throwables)

/**
* Sets a Throwable type to be thrown when the method is called. E.g:
Expand All @@ -73,7 +71,7 @@ case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce:
* @param throwableType to be thrown on method invocation
* @return object that allows stubbing consecutive calls
*/
def andThenThrow[E <: Throwable: ClassTag]: ScalaOngoingStubbing[T] = delegate thenThrow clazz
def andThenThrow[E <: Throwable: ClassTag]: ScalaOngoingStubbing[T] = _thenThrow

/**
* Sets the real implementation to be called when the method is called on a mock object.
Expand Down Expand Up @@ -104,63 +102,21 @@ case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce:
*
* @return object that allows stubbing consecutive calls
*/
def andThenCallRealMethod(): ScalaOngoingStubbing[T] = delegate.thenCallRealMethod()
def andThenCallRealMethod(): ScalaOngoingStubbing[T] = _thenCallRealMethod()

/**
* Sets a generic Answer for the method. E.g:
* <pre class="code"><code class="java">
* when(mock.someMethod(10)).thenAnswer(new Answer&lt;Integer&gt;() {
* public Integer answer(InvocationOnMock invocation) throws Throwable {
* return (Integer) invocation.getArguments()[0];
* }
* }
* </code></pre>
*
* @param answer the custom answer to execute.
* @return object that allows stubbing consecutive calls
*/
def andThenAnswer(f: => T): ScalaOngoingStubbing[T] = delegate thenAnswer invocationToAnswer(_ => f)
def andThenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = clazz[P0] match {
case c if c == classOf[InvocationOnMock] => delegate thenAnswer invocationToAnswer(i => f(i.asInstanceOf[P0]))
case _ => delegate thenAnswer functionToAnswer(f)
}
def andThenAnswer[P0, P1](f: (P0, P1) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2](f: (P0, P1, P2) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2, P3](f: (P0, P1, P2, P3) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4](f: (P0, P1, P2, P3, P4) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5](f: (P0, P1, P2, P3, P4, P5) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6](f: (P0, P1, P2, P3, P4, P5, P6) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6, P7](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
def andThenAnswer(f: => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1](f: (P0, P1) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2](f: (P0, P1, P2) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3](f: (P0, P1, P2, P3) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4](f: (P0, P1, P2, P3, P4) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5](f: (P0, P1, P2, P3, P4, P5) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6](f: (P0, P1, P2, P3, P4, P5, P6) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6, P7](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
_thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)
_thenAnswer(f)
def andThenAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10](
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): ScalaOngoingStubbing[T] =
delegate thenAnswer functionToAnswer(f)

/**
* Returns the mock that was used for this stub.
* <p>
* It allows to create a stub in one line of code.
* This can be helpful to keep test code clean.
* For example, some boring stub can be created & stubbed at field initialization in a test:
* <pre class="code"><code class="java">
* public class CarTest {
* Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();
*
* &#064;Test public void should... {}
* </code></pre>
*
* @param <M> The mock type given by the variable type.
* @return Mock used in this ongoing stubbing.
*/
def getMock[M]: M = delegate.getMock[M]
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): ScalaOngoingStubbing[T] = _thenAnswer(f)
}
Loading