Skip to content

Commit

Permalink
Modified hasCaptured Captor check to also inspect the number of exp…
Browse files Browse the repository at this point in the history
…ectations vs the number of captured values (#326)

Co-authored-by: Darren Bisop <darren.bishop@wejo.com>
  • Loading branch information
DarrenBishop and Darren Bisop authored Feb 10, 2021
1 parent 56bd5b1 commit ba7bf52
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 16 deletions.
27 changes: 17 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import sbt.Keys._

import scala.io.Source
import scala.language.postfixOps
import scala.util.Try
Expand Down Expand Up @@ -78,6 +76,17 @@ lazy val publishSettings = Seq(
)
)

lazy val noPublishingSettings = Seq(
publish := {},
publishLocal := {},
publishArtifact := false,
)

lazy val noCrossBuildSettings = Seq(
crossScalaVersions := Nil,
publish / skip := true
)

lazy val scalatest = (project in file("scalatest"))
.dependsOn(core)
.dependsOn(common % "compile-internal, test-internal")
Expand Down Expand Up @@ -134,14 +143,12 @@ lazy val common = (project in file("common"))
.dependsOn(macroCommon)
.settings(
commonSettings,
noPublishingSettings,
libraryDependencies ++= Dependencies.commonLibraries ++ Seq(
Dependencies.scalaReflection(scalaVersion.value),
Dependencies.catsLaws % "test",
Dependencies.scalacheck % "test"
),
publish := {},
publishLocal := {},
publishArtifact := false
)
)

lazy val core = (project in file("core"))
Expand Down Expand Up @@ -185,6 +192,7 @@ lazy val macroSub = (project in file("macro"))
.dependsOn(common)
.settings(
commonSettings,
noPublishingSettings,
libraryDependencies ++= Dependencies.commonLibraries,
libraryDependencies += Dependencies.scalaReflection(scalaVersion.value),
publish := {},
Expand All @@ -195,14 +203,13 @@ lazy val macroSub = (project in file("macro"))
lazy val macroCommon = (project in file("macro-common"))
.settings(
commonSettings,
noPublishingSettings,
libraryDependencies += Dependencies.scalaReflection(scalaVersion.value),
publish := {},
publishLocal := {},
publishArtifact := false
)

lazy val root = (project in file("."))
.settings(
publish := {},
publishLocal := {}
) aggregate (common, core, scalatest, specs2, cats, scalaz)
.settings(noPublishingSettings, noCrossBuildSettings)
.aggregate (common, core, scalatest, specs2, cats, scalaz)
30 changes: 25 additions & 5 deletions macro/src/main/scala/org/mockito/captor/Captor.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.mockito.captor

import org.mockito.internal.MacroDebug.debugResult
import org.mockito.exceptions.verification.ArgumentsAreDifferent
import org.mockito.exceptions.verification.{ ArgumentsAreDifferent, TooFewActualInvocations, TooManyActualInvocations }
import org.mockito.{ clazz, ArgumentCaptor }
import org.scalactic.Equality
import org.scalactic.TripleEquals._

import scala.collection.JavaConverters._
import scala.reflect.ClassTag
import scala.reflect.macros.blackbox
import scala.util.{ Failure, Try }

import org.mockito.exceptions.base.MockitoAssertionError

trait Captor[T] {
def capture: T
Expand All @@ -17,10 +19,28 @@ trait Captor[T] {

def values: List[T]

def hasCaptured(expectations: T*)(implicit $eq: Equality[T]): Unit =
expectations.zip(values).foreach { case (e, v) =>
if (e !== v) throw new ArgumentsAreDifferent(s"Got [$v] instead of [$e]")
def hasCaptured(expectations: T*)(implicit $eq: Equality[T]): Unit = {
val elementResult = Try {
expectations.zip(values).foreach { case (e, v) =>
if (e !== v) throw new ArgumentsAreDifferent(s"Got [$v] instead of [$e]")
}
}

val sizeResult = Try {
(expectations.size, values.size) match {
case (es, vs) if es - vs > 0 => throw new TooFewActualInvocations(s"Also expected ${es - vs} more: [${expectations.drop(vs).mkString(", ")}]")
case (es, vs) if es - vs < 0 => throw new TooManyActualInvocations(s"Also got ${vs - es} more: [${values.drop(es).mkString(", ")}]")
case _ => None
}
}

(elementResult, sizeResult) match {
case (Failure(ef), Failure(sf: MockitoAssertionError)) => throw new MockitoAssertionError(sf, ef.getMessage)
case (_, Failure(sf)) => throw sf
case (Failure(ef), _) => throw ef
case _ =>
}
}
}

class WrapperCaptor[T: ClassTag] extends Captor[T] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package user.org.mockito.captor

import org.mockito.captor.{ ArgCaptor, ValCaptor }
import org.mockito.captor.ArgCaptor
import org.mockito.exceptions.base.MockitoAssertionError
import org.mockito.exceptions.verification.{ TooFewActualInvocations, TooManyActualInvocations }
import org.mockito.{ IdiomaticMockito, MockitoSugar }
import org.scalactic.{ Equality, StringNormalizations }
import user.org.mockito.captor.ArgCaptorTest._
Expand Down Expand Up @@ -100,6 +102,81 @@ class ArgCaptorTest extends AnyWordSpec with MockitoSugar with Matchers {
captor.hasCaptured("it worked!", "it worked again!")
}

"report failure" when {

"fewer values were captured than expected" in {
val aMock = mock[Foo]
val captor = ArgCaptor[String]

aMock.stringArgument("it worked!")
aMock.stringArgument("it worked again!")

verify(aMock, times(2)).stringArgument(captor)

captor.values.should(contain).only("it worked!", "it worked again!")

the[TooManyActualInvocations] thrownBy {
captor.hasCaptured("it worked!")
} should have message "Also got 1 more: [it worked again!]"
}

"more values were captured than expected" in {
val aMock = mock[Foo]
val captor = ArgCaptor[String]

aMock.stringArgument("it worked!")

verify(aMock, times(1)).stringArgument(captor)

captor.values.should(contain).only("it worked!")

the[TooFewActualInvocations] thrownBy {
captor.hasCaptured("it worked!", "it worked again!")
} should have message "Also expected 1 more: [it worked again!]"
}

"fewer values were captured than expected while wrong values were captured" in {
val aMock = mock[Foo]
val captor = ArgCaptor[String]

aMock.stringArgument("it worked again!")

verify(aMock, times(1)).stringArgument(captor)

captor.values.should(contain).only("it worked again!")

val error = the[MockitoAssertionError] thrownBy {
captor.hasCaptured("it worked!", "it worked again!")
}

error.getMessage should (
include("Got [it worked again!] instead of [it worked!]") and
include("Also expected 1 more: [it worked again!]")
)
}

"more values were captured than expected while wrong values were captured" in {
val aMock = mock[Foo]
val captor = ArgCaptor[String]

aMock.stringArgument("it worked!")
aMock.stringArgument("it worked again!")

verify(aMock, times(2)).stringArgument(captor)

captor.values.should(contain).only("it worked!", "it worked again!")

val error = the[MockitoAssertionError] thrownBy {
captor.hasCaptured("it worked again!")
}

error.getMessage should (
include("Got [it worked!] instead of [it worked again!]") and
include("Also got 1 more: [it worked again!]")
)
}
}

"work with value case classes" in {
val aMock = mock[Foo]
val captor = ArgCaptor[Name]
Expand Down

0 comments on commit ba7bf52

Please sign in to comment.