Skip to content

Commit 478d926

Browse files
authored
Merge pull request #62 from mockito/return-value-classes
Adds support to return value classes from mocked methods
2 parents 4286116 + c5add6f commit 478d926

File tree

23 files changed

+427
-181
lines changed

23 files changed

+427
-181
lines changed

build.sbt

+19-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ lazy val commonSettings =
2626
"-Ypartial-unification",
2727
"-language:higherKinds",
2828
"-Xfatal-warnings",
29-
// "-Xmacro-settings:mockito-print-when,mockito-print-do-something,mockito-print-verify,mockito-print-captor,mockito-print-matcher"
29+
// "-Xmacro-settings:mockito-print-when,mockito-print-do-something,mockito-print-verify,mockito-print-captor,mockito-print-matcher,mockito-print-extractor"
3030
),
3131
)
3232

@@ -37,6 +37,7 @@ lazy val commonLibraries = Seq(
3737
)
3838

3939
lazy val common = (project in file("common"))
40+
.dependsOn(macroCommon)
4041
.settings(
4142
commonSettings,
4243
libraryDependencies ++= commonLibraries,
@@ -70,6 +71,14 @@ lazy val core = (project in file("core"))
7071
mappings in (Compile, packageSrc) ++= mappings
7172
.in(common, Compile, packageSrc)
7273
.value,
74+
// include the common classes and resources in the main jar
75+
mappings in (Compile, packageBin) ++= mappings
76+
.in(macroCommon, Compile, packageBin)
77+
.value,
78+
// include the common sources in the main source jar
79+
mappings in (Compile, packageSrc) ++= mappings
80+
.in(macroCommon, Compile, packageSrc)
81+
.value,
7382
licenses := Seq("MIT" -> url("https://opensource.org/licenses/MIT")),
7483
homepage := Some(url("https://github.com/mockito/mockito-scala")),
7584
scmInfo := Some(
@@ -99,6 +108,15 @@ lazy val macroSub = (project in file("macro"))
99108
publishArtifact := false
100109
)
101110

111+
lazy val macroCommon = (project in file("macro-common"))
112+
.settings(
113+
commonSettings,
114+
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
115+
publish := {},
116+
publishLocal := {},
117+
publishArtifact := false
118+
)
119+
102120
lazy val root = (project in file("."))
103121
.settings(
104122
publish := {},

common/src/main/scala/org/mockito/MockitoAPI.scala

+23-17
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.mockito
1313

14+
import org.mockito.internal.ValueClassExtractor
1415
import org.mockito.internal.configuration.plugins.Plugins.getMockMaker
1516
import org.mockito.internal.creation.MockSettingsImpl
1617
import org.mockito.internal.handler.ScalaMockHandler
@@ -56,10 +57,10 @@ private[mockito] trait DoSomething {
5657
* match argument types (`Type`)}}}
5758
*
5859
*/
59-
def doReturn[T](toBeReturned: T, toBeReturnedNext: T*): Stubber =
60+
def doReturn[T](toBeReturned: T, toBeReturnedNext: T*)(implicit $vce: ValueClassExtractor[T]): Stubber =
6061
Mockito.doReturn(
61-
toBeReturned,
62-
toBeReturnedNext.map(_.asInstanceOf[Object]): _*
62+
$vce.extract(toBeReturned),
63+
toBeReturnedNext.map($vce.extract).map(_.asInstanceOf[Object]): _*
6364
)
6465

6566
/**
@@ -88,31 +89,36 @@ private[mockito] trait DoSomething {
8889
/**
8990
* Delegates to <code>Mockito.doAnswer()</code>, it's only here to expose the full Mockito API
9091
*/
91-
def doAnswer[R](f: => R): Stubber =
92-
Mockito.doAnswer(invocationToAnswer(_ => f))
93-
def doAnswer[P0: ClassTag, R](f: P0 => R): Stubber = clazz[P0] match {
92+
def doAnswer[R: ValueClassExtractor](l: => R): Stubber =
93+
Mockito.doAnswer(invocationToAnswer(_ =>
94+
l match {
95+
case f: Function0[_] => f()
96+
case _ => l
97+
}))
98+
def doAnswer[P0: ClassTag, R: ValueClassExtractor](f: P0 => R): Stubber = clazz[P0] match {
9499
case c if c == classOf[InvocationOnMock] => Mockito.doAnswer(invocationToAnswer(i => f(i.asInstanceOf[P0])))
95100
case _ => Mockito.doAnswer(functionToAnswer(f))
96101
}
97-
def doAnswer[P0, P1, R](f: (P0, P1) => R): Stubber =
102+
def doAnswer[P0, P1, R: ValueClassExtractor](f: (P0, P1) => R): Stubber =
98103
Mockito.doAnswer(functionToAnswer(f))
99-
def doAnswer[P0, P1, P2, R](f: (P0, P1, P2) => R): Stubber =
104+
def doAnswer[P0, P1, P2, R: ValueClassExtractor](f: (P0, P1, P2) => R): Stubber =
100105
Mockito.doAnswer(functionToAnswer(f))
101-
def doAnswer[P0, P1, P2, P3, R](f: (P0, P1, P2, P3) => R): Stubber =
106+
def doAnswer[P0, P1, P2, P3, R: ValueClassExtractor](f: (P0, P1, P2, P3) => R): Stubber =
102107
Mockito.doAnswer(functionToAnswer(f))
103-
def doAnswer[P0, P1, P2, P3, P4, R](f: (P0, P1, P2, P3, P4) => R): Stubber =
108+
def doAnswer[P0, P1, P2, P3, P4, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4) => R): Stubber =
104109
Mockito.doAnswer(functionToAnswer(f))
105-
def doAnswer[P0, P1, P2, P3, P4, P5, R](f: (P0, P1, P2, P3, P4, P5) => R): Stubber =
110+
def doAnswer[P0, P1, P2, P3, P4, P5, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4, P5) => R): Stubber =
106111
Mockito.doAnswer(functionToAnswer(f))
107-
def doAnswer[P0, P1, P2, P3, P4, P5, P6, R](f: (P0, P1, P2, P3, P4, P5, P6) => R): Stubber =
112+
def doAnswer[P0, P1, P2, P3, P4, P5, P6, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4, P5, P6) => R): Stubber =
108113
Mockito.doAnswer(functionToAnswer(f))
109-
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, R](f: (P0, P1, P2, P3, P4, P5, P6, P7) => R): Stubber =
114+
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4, P5, P6, P7) => R): Stubber =
110115
Mockito.doAnswer(functionToAnswer(f))
111-
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, R](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => R): Stubber =
116+
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => R): Stubber =
112117
Mockito.doAnswer(functionToAnswer(f))
113-
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => R): Stubber =
118+
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R: ValueClassExtractor](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => R): Stubber =
114119
Mockito.doAnswer(functionToAnswer(f))
115-
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => R): Stubber =
120+
def doAnswer[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R: ValueClassExtractor](
121+
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => R): Stubber =
116122
Mockito.doAnswer(functionToAnswer(f))
117123
}
118124

@@ -325,7 +331,7 @@ private[mockito] trait Rest extends MockitoEnhancer with DoSomething with Verifi
325331
/**
326332
* Delegates to <code>Mockito.when()</code>, it's only here to expose the full Mockito API
327333
*/
328-
def when[T](methodCall: T): ScalaFirstStubbing[T] = Mockito.when(methodCall)
334+
def when[T: ValueClassExtractor](methodCall: T): ScalaFirstStubbing[T] = Mockito.when(methodCall)
329335

330336
/**
331337
* Delegates to <code>Mockito.ignoreStubs()</code>, it's only here to expose the full Mockito API

common/src/main/scala/org/mockito/mockito.scala

+21-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org
22

3+
import org.mockito.internal.ValueClassExtractor
34
import org.mockito.invocation.InvocationOnMock
45
import org.mockito.stubbing.Answer
56

@@ -9,36 +10,30 @@ package object mockito {
910
def clazz[T](implicit classTag: ClassTag[T]): Class[T] = classTag.runtimeClass.asInstanceOf[Class[T]]
1011

1112
//noinspection ConvertExpressionToSAM
12-
def invocationToAnswer[T](f: InvocationOnMock => T): Answer[T] = new Answer[T] {
13-
override def answer(invocation: InvocationOnMock): T = f(invocation)
13+
def invocationToAnswer[T](f: InvocationOnMock => T)(implicit $vce: ValueClassExtractor[T]): Answer[Any] = new Answer[Any] {
14+
override def answer(invocation: InvocationOnMock): Any = $vce.extract(f(invocation))
1415
}
1516

16-
def functionToAnswer[T, P0](f: P0 => T): Answer[T] = invocationToAnswer(i => f(i.getArgument[P0](0)))
17+
def functionToAnswer[T: ValueClassExtractor, P0](f: P0 => T): Answer[Any] =
18+
invocationToAnswer(i => f(i.getArgument[P0](0)))
1719

18-
def functionToAnswer[T, P1, P0](f: (P0, P1) => T): Answer[T] =
20+
def functionToAnswer[T: ValueClassExtractor, P1, P0](f: (P0, P1) => T): Answer[Any] =
1921
invocationToAnswer(i => f(i.getArgument[P0](0), i.getArgument[P1](1)))
2022

21-
def functionToAnswer[T, P2, P1, P0](f: (P0, P1, P2) => T): Answer[T] =
23+
def functionToAnswer[T: ValueClassExtractor, P2, P1, P0](f: (P0, P1, P2) => T): Answer[Any] =
2224
invocationToAnswer(i => f(i.getArgument[P0](0), i.getArgument[P1](1), i.getArgument[P2](2)))
2325

24-
def functionToAnswer[T, P3, P2, P1, P0](f: (P0, P1, P2, P3) => T): Answer[T] =
26+
def functionToAnswer[T: ValueClassExtractor, P3, P2, P1, P0](f: (P0, P1, P2, P3) => T): Answer[Any] =
2527
invocationToAnswer(i => f(i.getArgument[P0](0), i.getArgument[P1](1), i.getArgument[P2](2), i.getArgument[P3](3)))
2628

27-
def functionToAnswer[T, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4) => T): Answer[T] =
28-
invocationToAnswer(i =>
29-
f(i.getArgument[P0](0), i.getArgument[P1](1), i.getArgument[P2](2), i.getArgument[P3](3), i.getArgument[P4](4)))
29+
def functionToAnswer[T: ValueClassExtractor, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4) => T): Answer[Any] =
30+
invocationToAnswer(i => f(i.getArgument[P0](0), i.getArgument[P1](1), i.getArgument[P2](2), i.getArgument[P3](3), i.getArgument[P4](4)))
3031

31-
def functionToAnswer[T, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5) => T): Answer[T] =
32-
invocationToAnswer(
33-
i =>
34-
f(i.getArgument[P0](0),
35-
i.getArgument[P1](1),
36-
i.getArgument[P2](2),
37-
i.getArgument[P3](3),
38-
i.getArgument[P4](4),
39-
i.getArgument[P5](5)))
32+
def functionToAnswer[T: ValueClassExtractor, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5) => T): Answer[Any] =
33+
invocationToAnswer(i =>
34+
f(i.getArgument[P0](0), i.getArgument[P1](1), i.getArgument[P2](2), i.getArgument[P3](3), i.getArgument[P4](4), i.getArgument[P5](5)))
4035

41-
def functionToAnswer[T, P6, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5, P6) => T): Answer[T] =
36+
def functionToAnswer[T: ValueClassExtractor, P6, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5, P6) => T): Answer[Any] =
4237
invocationToAnswer(
4338
i =>
4439
f(i.getArgument[P0](0),
@@ -49,7 +44,7 @@ package object mockito {
4944
i.getArgument[P5](5),
5045
i.getArgument[P6](6)))
5146

52-
def functionToAnswer[T, P7, P6, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): Answer[T] =
47+
def functionToAnswer[T: ValueClassExtractor, P7, P6, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5, P6, P7) => T): Answer[Any] =
5348
invocationToAnswer(
5449
i =>
5550
f(
@@ -63,7 +58,8 @@ package object mockito {
6358
i.getArgument[P7](7)
6459
))
6560

66-
def functionToAnswer[T, P8, P7, P6, P5, P4, P3, P2, P1, P0](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): Answer[T] =
61+
def functionToAnswer[T: ValueClassExtractor, P8, P7, P6, P5, P4, P3, P2, P1, P0](
62+
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => T): Answer[Any] =
6763
invocationToAnswer(
6864
i =>
6965
f(
@@ -78,8 +74,8 @@ package object mockito {
7874
i.getArgument[P8](8)
7975
))
8076

81-
def functionToAnswer[T, P9, P8, P7, P6, P5, P4, P3, P2, P1, P0](
82-
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => T): Answer[T] =
77+
def functionToAnswer[T: ValueClassExtractor, P9, P8, P7, P6, P5, P4, P3, P2, P1, P0](
78+
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => T): Answer[Any] =
8379
invocationToAnswer(
8480
i =>
8581
f(
@@ -95,8 +91,8 @@ package object mockito {
9591
i.getArgument[P9](9)
9692
))
9793

98-
def functionToAnswer[T, P10, P9, P8, P7, P6, P5, P4, P3, P2, P1, P0](
99-
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): Answer[T] =
94+
def functionToAnswer[T: ValueClassExtractor, P10, P9, P8, P7, P6, P5, P4, P3, P2, P1, P0](
95+
f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): Answer[Any] =
10096
invocationToAnswer(
10197
i =>
10298
f(

common/src/main/scala/org/mockito/stubbing/ScalaFirstStubbing.scala

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package org.mockito.stubbing
22

3-
import org.mockito.{clazz, functionToAnswer, invocationToAnswer}
3+
import org.mockito.internal.ValueClassExtractor
4+
import org.mockito.{ clazz, functionToAnswer, invocationToAnswer }
45
import org.mockito.invocation.InvocationOnMock
56

67
import scala.language.implicitConversions
78
import scala.reflect.ClassTag
89

910
object ScalaFirstStubbing {
10-
implicit def toScalaFirstStubbing[T](v: OngoingStubbing[T]): ScalaFirstStubbing[T] = ScalaFirstStubbing(v)
11+
implicit def toScalaFirstStubbing[T: ValueClassExtractor](v: OngoingStubbing[T]): ScalaFirstStubbing[T] = ScalaFirstStubbing(v)
1112
}
1213

13-
case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T]) {
14+
case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: ValueClassExtractor[T]) {
1415

1516
/**
1617
* Sets consecutive return one or more values to be returned when the method is called. E.g:
@@ -29,7 +30,8 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T]) {
2930
* @param values next return values
3031
* @return object that allows stubbing consecutive calls
3132
*/
32-
def thenReturn(value: T, values: T*): ScalaOngoingStubbing[T] = delegate thenReturn (value, values: _*)
33+
def thenReturn(value: T, values: T*): ScalaOngoingStubbing[T] =
34+
delegate.thenReturn($vce.extract(value).asInstanceOf[T], values.map($vce.extract).map(_.asInstanceOf[T]): _*)
3335

3436
/**
3537
* Sets one or more Throwable objects to be thrown when the method is called. E.g:
@@ -105,7 +107,7 @@ case class ScalaFirstStubbing[T](delegate: OngoingStubbing[T]) {
105107
*
106108
* @return object that allows stubbing consecutive calls
107109
*/
108-
def thenCallRealMethod(): ScalaOngoingStubbing[T] = delegate thenCallRealMethod ()
110+
def thenCallRealMethod(): ScalaOngoingStubbing[T] = delegate.thenCallRealMethod()
109111

110112
def thenAnswer(f: => T): ScalaOngoingStubbing[T] = delegate thenAnswer invocationToAnswer(_ => f)
111113
def thenAnswer[P0: ClassTag](f: P0 => T): ScalaOngoingStubbing[T] = clazz[P0] match {

common/src/main/scala/org/mockito/stubbing/ScalaOngoingStubbing.scala

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package org.mockito.stubbing
22

33
import org.mockito._
4+
import org.mockito.internal.ValueClassExtractor
45
import org.mockito.invocation.InvocationOnMock
56

67
import scala.language.implicitConversions
78
import scala.reflect.ClassTag
89

910
object ScalaOngoingStubbing {
10-
implicit def toScalaOngoingStubbing[T](v: OngoingStubbing[T]): ScalaOngoingStubbing[T] = ScalaOngoingStubbing(v)
11+
implicit def toScalaOngoingStubbing[T: ValueClassExtractor](v: OngoingStubbing[T]): ScalaOngoingStubbing[T] = ScalaOngoingStubbing(v)
1112
}
1213

13-
case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T]) {
14+
case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T])(implicit $vce: ValueClassExtractor[T]) {
1415

1516
/**
1617
* Sets consecutive return values to be returned when the method is called. E.g:
@@ -26,7 +27,8 @@ case class ScalaOngoingStubbing[T](delegate: OngoingStubbing[T]) {
2627
* @param values next return values
2728
* @return object that allows stubbing consecutive calls
2829
*/
29-
def andThen(value: T, values: T*): ScalaOngoingStubbing[T] = delegate thenReturn (value, values: _*)
30+
def andThen(value: T, values: T*): ScalaOngoingStubbing[T] =
31+
delegate.thenReturn($vce.extract(value).asInstanceOf[T], values.map($vce.extract).map(_.asInstanceOf[T]): _*)
3032

3133
/**
3234
* Sets Throwable objects to be thrown when the method is called. E.g:

core/src/main/scala/org/mockito/ArgumentMatchersSugar.scala renamed to core/src/main/scala-2.11/org/mockito/ArgumentMatchersSugar.scala

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.mockito.matchers._
2525
trait ArgumentMatchersSugar
2626
extends AnyMatchers
2727
with EqMatchers
28+
with EqMatchers_211
2829
with ThatMatchers
2930
with StringThatMatchers
3031
with NullMatchers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.mockito.matchers
2+
3+
import org.scalactic.Equality
4+
5+
import scala.language.experimental.macros
6+
7+
trait EqMatchers_211 {
8+
9+
/**
10+
* Creates a matcher that delegates on {{org.scalactic.Equality}} so you can always customise how the values are compared
11+
* Also works with value classes
12+
*/
13+
def eqTo[T](value: T)(implicit eq: Equality[T]): T = macro MacroMatchers_211.eqToMatcher[T]
14+
15+
/**
16+
* It was intended to be used instead of eqTo when the argument is a value class,
17+
* but eqTo now supports value classes so it is not needed anymore
18+
*/
19+
@deprecated("Use 'eqTo' instead", since = "1.0.2")
20+
def eqToVal[T](value: T): T = macro MacroMatchers_211.eqToValMatcher[T]
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright © 2017 Morgan Stanley. All rights reserved.
3+
*
4+
* THIS SOFTWARE IS SUBJECT TO THE TERMS OF THE MIT LICENSE.
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
*
8+
* IN ADDITION, THE FOLLOWING DISCLAIMER APPLIES IN CONNECTION WITH THIS SOFTWARE:
9+
* THIS SOFTWARE IS LICENSED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY OF NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE MAY BE REDISTRIBUTED TO OTHERS ONLY BY EFFECTIVELY USING THIS OR ANOTHER EQUIVALENT DISCLAIMER IN ADDITION TO ANY OTHER REQUIRED LICENSE TERMS.
10+
*/
11+
12+
package org.mockito
13+
14+
import org.mockito.matchers._
15+
16+
/**
17+
* Trait that provides some syntax sugar and type mapping.
18+
*
19+
* It mostly forwards the calls to org.mockito.ArgumentMatchers, but with a few improvements to make it more scala-like
20+
* It also renames the "eq" matcher to "eqTo" as in Scala "eq" is a keyword used to do object identity equality
21+
*
22+
* @author Bruno Bonanno
23+
*
24+
*/
25+
trait ArgumentMatchersSugar
26+
extends AnyMatchers
27+
with EqMatchers
28+
with EqMatchers_212
29+
with ThatMatchers
30+
with StringThatMatchers
31+
with NullMatchers
32+
with FunctionMatchers
33+
with NumericMatchers
34+
with MacroBasedMatchers
35+
36+
/**
37+
* Simple object to allow the usage of the trait without mixing it in
38+
*
39+
* @author Bruno Bonanno
40+
*
41+
*/
42+
object ArgumentMatchersSugar extends ArgumentMatchersSugar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.mockito.matchers
2+
3+
import org.mockito.ArgumentMatcher
4+
import org.mockito.internal.ValueClassExtractor
5+
import org.scalactic.Equality
6+
7+
trait EqMatchers_212 {
8+
9+
/**
10+
* Creates a matcher that delegates on {{org.scalactic.Equality}} so you can always customise how the values are compared
11+
* Also works with value classes
12+
*/
13+
def eqTo[T](value: T)(implicit $eq: Equality[T], $vce: ValueClassExtractor[T]): T = {
14+
val extractedValue = $vce.extract(value)
15+
ThatMatchers.argThat(new ArgumentMatcher[T] {
16+
override def matches(v: T): Boolean = $eq.areEqual(extractedValue.asInstanceOf[T], v)
17+
override def toString: String = s"eqTo($value)"
18+
})
19+
value
20+
}
21+
22+
/**
23+
* It was intended to be used instead of eqTo when the argument is a value class,
24+
* but eqTo now supports value classes so it is not needed anymore
25+
*/
26+
@deprecated("Use 'eqTo' instead", since = "1.0.2")
27+
def eqToVal[T](value: T)(implicit $eq: Equality[T], $vce: ValueClassExtractor[T]): T = eqTo(value)
28+
}

0 commit comments

Comments
 (0)