Skip to content

Commit ba7bf52

Browse files
DarrenBishopDarren Bisop
andauthored
Modified hasCaptured Captor check to also inspect the number of expectations vs the number of captured values (#326)
Co-authored-by: Darren Bisop <[email protected]>
1 parent 56bd5b1 commit ba7bf52

File tree

3 files changed

+120
-16
lines changed

3 files changed

+120
-16
lines changed

build.sbt

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import sbt.Keys._
2-
31
import scala.io.Source
42
import scala.language.postfixOps
53
import scala.util.Try
@@ -78,6 +76,17 @@ lazy val publishSettings = Seq(
7876
)
7977
)
8078

79+
lazy val noPublishingSettings = Seq(
80+
publish := {},
81+
publishLocal := {},
82+
publishArtifact := false,
83+
)
84+
85+
lazy val noCrossBuildSettings = Seq(
86+
crossScalaVersions := Nil,
87+
publish / skip := true
88+
)
89+
8190
lazy val scalatest = (project in file("scalatest"))
8291
.dependsOn(core)
8392
.dependsOn(common % "compile-internal, test-internal")
@@ -134,14 +143,12 @@ lazy val common = (project in file("common"))
134143
.dependsOn(macroCommon)
135144
.settings(
136145
commonSettings,
146+
noPublishingSettings,
137147
libraryDependencies ++= Dependencies.commonLibraries ++ Seq(
138148
Dependencies.scalaReflection(scalaVersion.value),
139149
Dependencies.catsLaws % "test",
140150
Dependencies.scalacheck % "test"
141-
),
142-
publish := {},
143-
publishLocal := {},
144-
publishArtifact := false
151+
)
145152
)
146153

147154
lazy val core = (project in file("core"))
@@ -185,6 +192,7 @@ lazy val macroSub = (project in file("macro"))
185192
.dependsOn(common)
186193
.settings(
187194
commonSettings,
195+
noPublishingSettings,
188196
libraryDependencies ++= Dependencies.commonLibraries,
189197
libraryDependencies += Dependencies.scalaReflection(scalaVersion.value),
190198
publish := {},
@@ -195,14 +203,13 @@ lazy val macroSub = (project in file("macro"))
195203
lazy val macroCommon = (project in file("macro-common"))
196204
.settings(
197205
commonSettings,
206+
noPublishingSettings,
198207
libraryDependencies += Dependencies.scalaReflection(scalaVersion.value),
199208
publish := {},
200209
publishLocal := {},
201210
publishArtifact := false
202211
)
203212

204213
lazy val root = (project in file("."))
205-
.settings(
206-
publish := {},
207-
publishLocal := {}
208-
) aggregate (common, core, scalatest, specs2, cats, scalaz)
214+
.settings(noPublishingSettings, noCrossBuildSettings)
215+
.aggregate (common, core, scalatest, specs2, cats, scalaz)

macro/src/main/scala/org/mockito/captor/Captor.scala

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package org.mockito.captor
22

33
import org.mockito.internal.MacroDebug.debugResult
4-
import org.mockito.exceptions.verification.ArgumentsAreDifferent
4+
import org.mockito.exceptions.verification.{ ArgumentsAreDifferent, TooFewActualInvocations, TooManyActualInvocations }
55
import org.mockito.{ clazz, ArgumentCaptor }
66
import org.scalactic.Equality
77
import org.scalactic.TripleEquals._
8-
98
import scala.collection.JavaConverters._
109
import scala.reflect.ClassTag
1110
import scala.reflect.macros.blackbox
11+
import scala.util.{ Failure, Try }
12+
13+
import org.mockito.exceptions.base.MockitoAssertionError
1214

1315
trait Captor[T] {
1416
def capture: T
@@ -17,10 +19,28 @@ trait Captor[T] {
1719

1820
def values: List[T]
1921

20-
def hasCaptured(expectations: T*)(implicit $eq: Equality[T]): Unit =
21-
expectations.zip(values).foreach { case (e, v) =>
22-
if (e !== v) throw new ArgumentsAreDifferent(s"Got [$v] instead of [$e]")
22+
def hasCaptured(expectations: T*)(implicit $eq: Equality[T]): Unit = {
23+
val elementResult = Try {
24+
expectations.zip(values).foreach { case (e, v) =>
25+
if (e !== v) throw new ArgumentsAreDifferent(s"Got [$v] instead of [$e]")
26+
}
2327
}
28+
29+
val sizeResult = Try {
30+
(expectations.size, values.size) match {
31+
case (es, vs) if es - vs > 0 => throw new TooFewActualInvocations(s"Also expected ${es - vs} more: [${expectations.drop(vs).mkString(", ")}]")
32+
case (es, vs) if es - vs < 0 => throw new TooManyActualInvocations(s"Also got ${vs - es} more: [${values.drop(es).mkString(", ")}]")
33+
case _ => None
34+
}
35+
}
36+
37+
(elementResult, sizeResult) match {
38+
case (Failure(ef), Failure(sf: MockitoAssertionError)) => throw new MockitoAssertionError(sf, ef.getMessage)
39+
case (_, Failure(sf)) => throw sf
40+
case (Failure(ef), _) => throw ef
41+
case _ =>
42+
}
43+
}
2444
}
2545

2646
class WrapperCaptor[T: ClassTag] extends Captor[T] {

scalatest/src/test/scala/user/org/mockito/captor/ArgCaptorTest.scala

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package user.org.mockito.captor
22

3-
import org.mockito.captor.{ ArgCaptor, ValCaptor }
3+
import org.mockito.captor.ArgCaptor
4+
import org.mockito.exceptions.base.MockitoAssertionError
5+
import org.mockito.exceptions.verification.{ TooFewActualInvocations, TooManyActualInvocations }
46
import org.mockito.{ IdiomaticMockito, MockitoSugar }
57
import org.scalactic.{ Equality, StringNormalizations }
68
import user.org.mockito.captor.ArgCaptorTest._
@@ -100,6 +102,81 @@ class ArgCaptorTest extends AnyWordSpec with MockitoSugar with Matchers {
100102
captor.hasCaptured("it worked!", "it worked again!")
101103
}
102104

105+
"report failure" when {
106+
107+
"fewer values were captured than expected" in {
108+
val aMock = mock[Foo]
109+
val captor = ArgCaptor[String]
110+
111+
aMock.stringArgument("it worked!")
112+
aMock.stringArgument("it worked again!")
113+
114+
verify(aMock, times(2)).stringArgument(captor)
115+
116+
captor.values.should(contain).only("it worked!", "it worked again!")
117+
118+
the[TooManyActualInvocations] thrownBy {
119+
captor.hasCaptured("it worked!")
120+
} should have message "Also got 1 more: [it worked again!]"
121+
}
122+
123+
"more values were captured than expected" in {
124+
val aMock = mock[Foo]
125+
val captor = ArgCaptor[String]
126+
127+
aMock.stringArgument("it worked!")
128+
129+
verify(aMock, times(1)).stringArgument(captor)
130+
131+
captor.values.should(contain).only("it worked!")
132+
133+
the[TooFewActualInvocations] thrownBy {
134+
captor.hasCaptured("it worked!", "it worked again!")
135+
} should have message "Also expected 1 more: [it worked again!]"
136+
}
137+
138+
"fewer values were captured than expected while wrong values were captured" in {
139+
val aMock = mock[Foo]
140+
val captor = ArgCaptor[String]
141+
142+
aMock.stringArgument("it worked again!")
143+
144+
verify(aMock, times(1)).stringArgument(captor)
145+
146+
captor.values.should(contain).only("it worked again!")
147+
148+
val error = the[MockitoAssertionError] thrownBy {
149+
captor.hasCaptured("it worked!", "it worked again!")
150+
}
151+
152+
error.getMessage should (
153+
include("Got [it worked again!] instead of [it worked!]") and
154+
include("Also expected 1 more: [it worked again!]")
155+
)
156+
}
157+
158+
"more values were captured than expected while wrong values were captured" in {
159+
val aMock = mock[Foo]
160+
val captor = ArgCaptor[String]
161+
162+
aMock.stringArgument("it worked!")
163+
aMock.stringArgument("it worked again!")
164+
165+
verify(aMock, times(2)).stringArgument(captor)
166+
167+
captor.values.should(contain).only("it worked!", "it worked again!")
168+
169+
val error = the[MockitoAssertionError] thrownBy {
170+
captor.hasCaptured("it worked again!")
171+
}
172+
173+
error.getMessage should (
174+
include("Got [it worked!] instead of [it worked again!]") and
175+
include("Also got 1 more: [it worked again!]")
176+
)
177+
}
178+
}
179+
103180
"work with value case classes" in {
104181
val aMock = mock[Foo]
105182
val captor = ArgCaptor[Name]

0 commit comments

Comments
 (0)