Skip to content

Add argMatching[T] #64

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
Nov 27, 2018
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ This trait exposes all the existent `org.mockito.ArgumentMatchers` but again it
* `isNull` and `isNotNull` are deprecated as using nulls in Scala is clear code smell
* Adds support for value classes via `anyVal[T]` and `eqToVal[T]()` **NOTE: both had been deprecated (use `any[T]` or `eqTo[T]` instead)**
* Adds `function0` to easily match for a function that returns a given value
* Adds `argMatching` that takes a partial function to match, i.e. `argMatching({ case Baz(_, "pepe") => })`

Again, the companion object also extends the trait to allow the usage of the API without mixing-in the trait in case that's desired

Expand Down
5 changes: 5 additions & 0 deletions common/src/main/scala/org/mockito/matchers/ThatMatchers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ private[mockito] trait ThatMatchers {
*/
def longThat(matcher: ArgumentMatcher[Long]): Long = argThat(matcher)

def argMatching[T](pf: PartialFunction[Any, Unit]) =
argThat[T](new ArgumentMatcher[T] {
override def matches(argument: T): Boolean = pf.isDefinedAt(argument)
override def toString: String = "argMatching(...)"
})
}

private[mockito] object ThatMatchers extends ThatMatchers
30 changes: 23 additions & 7 deletions core/src/test/scala/user/org/mockito/IdiomaticMockitoTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package user.org.mockito
import org.mockito.captor.ArgCaptor
import org.mockito.exceptions.verification._
import org.mockito.invocation.InvocationOnMock
import org.mockito.{ArgumentMatchersSugar, IdiomaticMockito}
import org.scalatest.{Matchers, WordSpec}
import user.org.mockito.matchers.{ValueCaseClass, ValueClass}
import org.mockito.{ ArgumentMatchersSugar, IdiomaticMockito }
import org.scalatest.{ Matchers, WordSpec }
import user.org.mockito.matchers.{ ValueCaseClass, ValueClass }

class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito with ArgumentMatchersSugar {

Expand Down Expand Up @@ -36,12 +36,16 @@ class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito
def valueCaseClass(n: Int, v: ValueCaseClass): String = ???

def returnsValueCaseClass: ValueCaseClass = ???

def baz(i: Int, b: Baz): String = ???
}

class Bar {
def iHaveDefaultArgs(v: String = "default"): String = v
}

case class Baz(param1: Int, param2: String)

"StubbingOps" should {
"stub a return value" in {
val org = mock[Org]
Expand Down Expand Up @@ -119,8 +123,8 @@ class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito
org.doSomethingWithThisInt(*) shouldAnswer ((i: Int) => i * 10 + 2)
org.doSomethingWithThisIntAndString(*, *) shouldAnswer ((i: Int, s: String) => (i * 10 + s.toInt).toString)
org.doSomethingWithThisIntAndStringAndBoolean(*, *, *) shouldAnswer ((i: Int,
s: String,
boolean: Boolean) => (i * 10 + s.toInt).toString + boolean)
s: String,
boolean: Boolean) => (i * 10 + s.toInt).toString + boolean)

org.doSomethingWithThisInt(4) shouldBe 42
org.doSomethingWithThisIntAndString(4, "2") shouldBe "42"
Expand Down Expand Up @@ -404,7 +408,7 @@ class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito
}

"work with a captor" in {
val org = mock[Org]
val org = mock[Org]
val argCaptor = ArgCaptor[Int]

org.doSomethingWithThisIntAndString(42, "test")
Expand Down Expand Up @@ -441,7 +445,7 @@ class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito

"mix arguments and raw parameters" should {
"create a mock where I can mix matchers, normal and implicit parameters" in {
val org = mock[Org]
val org = mock[Org]
implicit val implicitValue: Implicit[Int] = mock[Implicit[Int]]

org.iHaveTypeParamsAndImplicits[Int, String](*, "test") shouldReturn "mocked!"
Expand Down Expand Up @@ -505,6 +509,18 @@ class IdiomaticMockitoTest extends WordSpec with Matchers with IdiomaticMockito
org.valueCaseClass(*, ValueCaseClass(200)) was called
}

"argMatching works with new syntax" in {
val org = mock[Org]

org.baz(2, argMatching({ case Baz(n, _) if n > 90 => })) shouldReturn "mocked!"
org.baz(2, Baz(100, "pepe")) shouldBe "mocked!"
org.baz(2, argMatching({ case Baz(_, "pepe") => })) was called

an[WantedButNotInvoked] should be thrownBy {
org.baz(2, argMatching({ case Baz(99, "pepe") => })) was called
}
}

"anyVal works with new syntax" in {
val org = mock[Org]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package user.org.mockito.matchers

import org.mockito.{ArgumentMatcher, ArgumentMatchersSugar, MockitoSugar}
import org.scalatest.{FlatSpec, Matchers => ScalaTestMatchers}

class ThatMatchersTest extends FlatSpec with MockitoSugar with ScalaTestMatchers with ArgumentMatchersSugar {
import org.mockito.exceptions.verification.WantedButNotInvoked
import org.mockito.{ ArgumentMatcher, ArgumentMatchersSugar, MockitoSugar }
import org.scalatest.{ FlatSpec, Matchers => ScalaTestMatchers }

object ThatMatchersTest {
class EqTo[T](value: T) extends ArgumentMatcher[T] {
override def matches(argument: T): Boolean = argument == value
}
Expand Down Expand Up @@ -34,6 +34,34 @@ class ThatMatchersTest extends FlatSpec with MockitoSugar with ScalaTestMatchers

def baz(v: Baz): Baz = v
}
}

class ThatMatchersTest extends FlatSpec with MockitoSugar with ScalaTestMatchers with ArgumentMatchersSugar {

import ThatMatchersTest._

"argMatching[T]" should "work in various scenarios" in {
val aMock = mock[Foo]

aMock.bar("meh")
verify(aMock).bar(argMatching({ case "meh" => }))

aMock.barTyped("meh")
verify(aMock).barTyped(argMatching({ case "meh" => }))

aMock.bar(List("meh"))
verify(aMock).bar(argMatching({ case "meh" :: Nil => }))

aMock.baz(Baz("Hello", "World"))
verify(aMock).baz(argMatching({ case Baz("Hello", "World") => }))
verify(aMock).baz(argMatching({ case Baz(_, "World") => }))
verify(aMock).baz(argMatching({ case Baz("Hello", _) => }))
verify(aMock).baz(argMatching({ case Baz(_, _) => }))

an[WantedButNotInvoked] should be thrownBy {
verify(aMock).baz(argMatching({ case Baz("", _) => }))
}
}

"argThat[T]" should "work with AnyRef" in {
val aMock = mock[Foo]
Expand Down
1 change: 1 addition & 0 deletions macro/src/main/scala/org/mockito/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ object Utils {
case q"$_.floatThat[$_]($_)" => true
case q"$_.shortThat[$_]($_)" => true
case q"$_.longThat[$_]($_)" => true
case q"$_.argMatching[$_]($_)" => true

case q"$_.n.>[$_]($_)($_)" => true
case q"$_.n.>=[$_]($_)($_)" => true
Expand Down