Skip to content

Commit e84e547

Browse files
committed
New "expect" DSL promoting safer prefixed verifications, fixes #198
With existing postfix verifications, it is easy to overlook a missing verification when method signatures are long, so "was called" statements can be hard to spot - and unfortunately, in a lot of cases it means that a test that should be failing starts to pass. New DSL helps with this situation by sort of going back to the roots of vanilla Mockito, where "verify" word is defined before the verified method signature. Macros help us solve the inconvenience of having to wrap the mock variable with parentheses. Better naming wanted for IdiomaticMockito2 :)
1 parent 242a12a commit e84e547

File tree

12 files changed

+1447
-160
lines changed

12 files changed

+1447
-160
lines changed

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ lazy val commonSettings =
3030
"-encoding", "UTF-8",
3131
"-Xfatal-warnings",
3232
"-language:reflectiveCalls,implicitConversions,experimental.macros,higherKinds",
33-
// "-Xmacro-settings:mockito-print-when,mockito-print-do-something,mockito-print-verify,mockito-print-captor,mockito-print-matcher,mockito-print-extractor,mockito-print-wrapper,mockito-print-lenient"
33+
// "-Xmacro-settings:mockito-print-when,mockito-print-do-something,mockito-print-verify,mockito-print-expect,mockito-print-captor,mockito-print-matcher,mockito-print-extractor,mockito-print-wrapper,mockito-print-lenient"
3434
),
3535
scalacOptions ++= {
3636
CrossVersion.partialVersion(scalaVersion.value) match {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.mockito
22

3-
trait IdiomaticMockito extends IdiomaticMockitoBase {
3+
trait IdiomaticMockito extends IdiomaticStubbing with PostfixVerifications {
44
override type Verification = Unit
55
override def verification(v: => Any): Verification = v
66
}
@@ -9,3 +9,14 @@ trait IdiomaticMockito extends IdiomaticMockitoBase {
99
* Simple object to allow the usage of the trait without mixing it in
1010
*/
1111
object IdiomaticMockito extends IdiomaticMockito
12+
13+
// TODO need a better name
14+
trait IdiomaticMockito2 extends IdiomaticStubbing with PrefixExpectations {
15+
override type Verification = Unit
16+
override def verification(v: => Any): Verification = v
17+
}
18+
19+
/**
20+
* Simple object to allow the usage of the trait without mixing it in
21+
*/
22+
object IdiomaticMockito2 extends IdiomaticMockito2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.mockito
22

3-
import org.mockito.WhenMacro._
43
import org.mockito.stubbing.{ ScalaFirstStubbing, ScalaOngoingStubbing }
54
import org.mockito.verification.VerificationMode
65

@@ -84,151 +83,12 @@ object IdiomaticMockitoBase {
8483
class ThrowActions[T](os: ScalaFirstStubbing[T]) {
8584
def apply[E <: Throwable](e: E*): ScalaOngoingStubbing[T] = os thenThrow (e: _*)
8685
}
87-
}
88-
89-
trait IdiomaticMockitoBase extends MockitoEnhancer with ScalacticSerialisableHack {
90-
import org.mockito.IdiomaticMockitoBase._
91-
92-
type Verification
93-
94-
def verification(v: => Any): Verification
95-
96-
implicit class StubbingOps[T](stubbing: T) {
97-
def shouldReturn: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
98-
def mustReturn: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
99-
def returns: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
100-
101-
def shouldCall(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
102-
def mustCall(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
103-
def calls(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
104-
105-
def shouldThrow: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
106-
def mustThrow: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
107-
def throws: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
108-
109-
def shouldAnswer: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
110-
def mustAnswer: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
111-
def answers: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
112-
113-
def shouldAnswerPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
114-
def mustAnswerPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
115-
def answersPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
116-
117-
//noinspection AccessorLikeMethodIsUnit
118-
def isLenient(): Unit = macro WhenMacro.isLenient[T]
119-
120-
def shouldDoNothing(): Unit = macro DoSomethingMacro.doesNothing
121-
def mustDoNothing(): Unit = macro DoSomethingMacro.doesNothing
122-
def doesNothing(): Unit = macro DoSomethingMacro.doesNothing
123-
}
124-
125-
implicit class VerifyingOps[T](stubbing: T) {
126-
def was(called: Called.type)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
127-
128-
def wasNever(called: Called.type)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
129-
130-
def wasNever(called: CalledAgain)(implicit $ev: T <:< AnyRef): Verification =
131-
macro VerifyMacro.wasNeverCalledAgainMacro[T, Verification]
132-
133-
def wasCalled(called: ScalaVerificationMode)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
134-
}
135-
136-
val called: Called.type = Called
137-
val thrown: Thrown.type = Thrown
138-
val returned: Returned.type = Returned
139-
val answered: Answered.type = Answered
140-
val theRealMethod: RealMethod.type = RealMethod
141-
142-
implicit class DoSomethingOps[R](v: R) {
143-
def willBe(r: Returned.type): ReturnedBy[R] = ReturnedBy[R]()
144-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
145-
}
14686

147-
implicit class DoSomethingOps0[R](v: () => R) {
148-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
149-
}
150-
implicit class DoSomethingOps1[P0, R](v: P0 => R) {
151-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
152-
}
153-
implicit class DoSomethingOps2[P0, P1, R](v: (P0, P1) => R) {
154-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
155-
}
156-
implicit class DoSomethingOps3[P0, P1, P2, R](v: (P0, P1, P2) => R) {
157-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
158-
}
159-
implicit class DoSomethingOps4[P0, P1, P2, P3, R](v: (P0, P1, P2, P3) => R) {
160-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
161-
}
162-
implicit class DoSomethingOps5[P0, P1, P2, P3, P4, R](v: (P0, P1, P2, P3, P4) => R) {
163-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
164-
}
165-
implicit class DoSomethingOps6[P0, P1, P2, P3, P4, P5, R](v: (P0, P1, P2, P3, P4, P5) => R) {
166-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
167-
}
168-
implicit class DoSomethingOps7[P0, P1, P2, P3, P4, P5, P6, R](v: (P0, P1, P2, P3, P4, P5, P6) => R) {
169-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
170-
}
171-
implicit class DoSomethingOps8[P0, P1, P2, P3, P4, P5, P6, P7, R](v: (P0, P1, P2, P3, P4, P5, P6, P7) => R) {
172-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
173-
}
174-
implicit class DoSomethingOps9[P0, P1, P2, P3, P4, P5, P6, P7, P8, R](v: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => R) {
175-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
176-
}
177-
implicit class DoSomethingOps10[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R](v: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => R) {
178-
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
179-
}
180-
181-
implicit class ThrowSomethingOps[E](v: E) {
182-
def willBe(thrown: Thrown.type): ThrownBy[E] = new ThrownBy[E]
183-
}
184-
185-
val calledAgain: CalledAgain.type = CalledAgain
186-
val ignoringStubs: IgnoringStubs.type = IgnoringStubs
187-
188-
val realMethod: RealMethod.type = RealMethod
189-
190-
val on: On.type = On
191-
val onlyHere: OnlyOn.type = OnlyOn
192-
val once: Times = Times(1)
193-
val twice: Times = Times(2)
194-
val thrice: Times = Times(3)
195-
val threeTimes: Times = Times(3)
196-
val fourTimes: Times = Times(4)
197-
val fiveTimes: Times = Times(5)
198-
val sixTimes: Times = Times(6)
199-
val sevenTimes: Times = Times(7)
200-
val eightTimes: Times = Times(8)
201-
val nineTimes: Times = Times(9)
202-
val tenTimes: Times = Times(10)
203-
val atLeastOnce: AtLeast = AtLeast(1)
204-
val atLeastTwice: AtLeast = AtLeast(2)
205-
val atLeastThrice: AtLeast = AtLeast(3)
206-
val atLeastThreeTimes: AtLeast = AtLeast(3)
207-
val atLeastFourTimes: AtLeast = AtLeast(4)
208-
val atLeastFiveTimes: AtLeast = AtLeast(5)
209-
val atLeastSixTimes: AtLeast = AtLeast(6)
210-
val atLeastSevenTimes: AtLeast = AtLeast(7)
211-
val atLeastEightTimes: AtLeast = AtLeast(8)
212-
val atLeastNineTimes: AtLeast = AtLeast(9)
213-
val atLeastTenTimes: AtLeast = AtLeast(10)
214-
val atMostOnce: AtMost = AtMost(1)
215-
val atMostTwice: AtMost = AtMost(2)
216-
val atMostThrice: AtMost = AtMost(3)
217-
val atMostThreeTimes: AtMost = AtMost(3)
218-
val atMostFourTimes: AtMost = AtMost(4)
219-
val atMostFiveTimes: AtMost = AtMost(5)
220-
val atMostSixTimes: AtMost = AtMost(6)
221-
val atMostSevenTimes: AtMost = AtMost(7)
222-
val atMostEightTimes: AtMost = AtMost(8)
223-
val atMostNineTimes: AtMost = AtMost(9)
224-
val atMostTenTimes: AtMost = AtMost(10)
225-
226-
def InOrder(mocks: AnyRef*)(verifications: VerifyInOrder => Verification): Verification = verifications(VerifyInOrder(mocks))
227-
228-
def atLeast(t: Times): AtLeast = AtLeast(t.times)
229-
def atMost(t: Times): AtMost = AtMost(t.times)
230-
231-
implicit class IntOps(i: Int) {
232-
def times: Times = Times(i)
87+
// types for postfix verifications
88+
object CallWord
89+
object CallsWord {
90+
def apply(ignoringStubsWord: IgnoringStubs.type): CallsWord.type = this
23391
}
23492
}
93+
94+
trait IdiomaticMockitoBase extends IdiomaticStubbing with PostfixVerifications
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.mockito
2+
3+
import org.mockito.WhenMacro._
4+
import org.mockito.stubbing.ScalaOngoingStubbing
5+
6+
trait IdiomaticStubbing extends MockitoEnhancer with ScalacticSerialisableHack {
7+
import org.mockito.IdiomaticMockitoBase._
8+
9+
implicit class StubbingOps[T](stubbing: T) {
10+
def shouldReturn: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
11+
def mustReturn: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
12+
def returns: ReturnActions[T] = macro WhenMacro.shouldReturn[T]
13+
14+
def shouldCall(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
15+
def mustCall(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
16+
def calls(crm: RealMethod.type): ScalaOngoingStubbing[T] = macro WhenMacro.shouldCallRealMethod[T]
17+
18+
def shouldThrow: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
19+
def mustThrow: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
20+
def throws: ThrowActions[T] = macro WhenMacro.shouldThrow[T]
21+
22+
def shouldAnswer: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
23+
def mustAnswer: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
24+
def answers: AnswerActions[T] = macro WhenMacro.shouldAnswer[T]
25+
26+
def shouldAnswerPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
27+
def mustAnswerPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
28+
def answersPF: AnswerPFActions[T] = macro WhenMacro.shouldAnswerPF[T]
29+
30+
//noinspection AccessorLikeMethodIsUnit
31+
def isLenient(): Unit = macro WhenMacro.isLenient[T]
32+
33+
def shouldDoNothing(): Unit = macro DoSomethingMacro.doesNothing
34+
def mustDoNothing(): Unit = macro DoSomethingMacro.doesNothing
35+
def doesNothing(): Unit = macro DoSomethingMacro.doesNothing
36+
}
37+
38+
val called: Called.type = Called
39+
val thrown: Thrown.type = Thrown
40+
val returned: Returned.type = Returned
41+
val answered: Answered.type = Answered
42+
val theRealMethod: RealMethod.type = RealMethod
43+
44+
val realMethod: RealMethod.type = RealMethod
45+
46+
implicit class DoSomethingOps[R](v: R) {
47+
def willBe(r: Returned.type): ReturnedBy[R] = ReturnedBy[R]()
48+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
49+
}
50+
51+
implicit class DoSomethingOps0[R](v: () => R) {
52+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
53+
}
54+
implicit class DoSomethingOps1[P0, R](v: P0 => R) {
55+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
56+
}
57+
implicit class DoSomethingOps2[P0, P1, R](v: (P0, P1) => R) {
58+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
59+
}
60+
implicit class DoSomethingOps3[P0, P1, P2, R](v: (P0, P1, P2) => R) {
61+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
62+
}
63+
implicit class DoSomethingOps4[P0, P1, P2, P3, R](v: (P0, P1, P2, P3) => R) {
64+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
65+
}
66+
implicit class DoSomethingOps5[P0, P1, P2, P3, P4, R](v: (P0, P1, P2, P3, P4) => R) {
67+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
68+
}
69+
implicit class DoSomethingOps6[P0, P1, P2, P3, P4, P5, R](v: (P0, P1, P2, P3, P4, P5) => R) {
70+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
71+
}
72+
implicit class DoSomethingOps7[P0, P1, P2, P3, P4, P5, P6, R](v: (P0, P1, P2, P3, P4, P5, P6) => R) {
73+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
74+
}
75+
implicit class DoSomethingOps8[P0, P1, P2, P3, P4, P5, P6, P7, R](v: (P0, P1, P2, P3, P4, P5, P6, P7) => R) {
76+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
77+
}
78+
implicit class DoSomethingOps9[P0, P1, P2, P3, P4, P5, P6, P7, P8, R](v: (P0, P1, P2, P3, P4, P5, P6, P7, P8) => R) {
79+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
80+
}
81+
implicit class DoSomethingOps10[P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R](v: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9) => R) {
82+
def willBe(a: Answered.type): AnsweredBy[R] = AnsweredBy[R]()
83+
}
84+
85+
implicit class ThrowSomethingOps[E](v: E) {
86+
def willBe(thrown: Thrown.type): ThrownBy[E] = new ThrownBy[E]
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.mockito
2+
3+
trait PostfixVerifications {
4+
5+
import org.mockito.IdiomaticMockitoBase._
6+
7+
type Verification
8+
9+
def verification(v: => Any): Verification
10+
11+
implicit class VerifyingOps[T](stubbing: T) {
12+
def was(called: Called.type)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
13+
14+
def wasNever(called: Called.type)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
15+
16+
def wasNever(called: CalledAgain)(implicit $ev: T <:< AnyRef): Verification =
17+
macro VerifyMacro.wasNeverCalledAgainMacro[T, Verification]
18+
19+
def wasCalled(called: ScalaVerificationMode)(implicit order: VerifyOrder): Verification = macro VerifyMacro.wasMacro[T, Verification]
20+
}
21+
22+
val calledAgain: CalledAgain.type = CalledAgain
23+
val ignoringStubs: IgnoringStubs.type = IgnoringStubs
24+
25+
val on: On.type = On
26+
val onlyHere: OnlyOn.type = OnlyOn
27+
val once: Times = Times(1)
28+
val twice: Times = Times(2)
29+
val thrice: Times = Times(3)
30+
val threeTimes: Times = Times(3)
31+
val fourTimes: Times = Times(4)
32+
val fiveTimes: Times = Times(5)
33+
val sixTimes: Times = Times(6)
34+
val sevenTimes: Times = Times(7)
35+
val eightTimes: Times = Times(8)
36+
val nineTimes: Times = Times(9)
37+
val tenTimes: Times = Times(10)
38+
val atLeastOnce: AtLeast = AtLeast(1)
39+
val atLeastTwice: AtLeast = AtLeast(2)
40+
val atLeastThrice: AtLeast = AtLeast(3)
41+
val atLeastThreeTimes: AtLeast = AtLeast(3)
42+
val atLeastFourTimes: AtLeast = AtLeast(4)
43+
val atLeastFiveTimes: AtLeast = AtLeast(5)
44+
val atLeastSixTimes: AtLeast = AtLeast(6)
45+
val atLeastSevenTimes: AtLeast = AtLeast(7)
46+
val atLeastEightTimes: AtLeast = AtLeast(8)
47+
val atLeastNineTimes: AtLeast = AtLeast(9)
48+
val atLeastTenTimes: AtLeast = AtLeast(10)
49+
val atMostOnce: AtMost = AtMost(1)
50+
val atMostTwice: AtMost = AtMost(2)
51+
val atMostThrice: AtMost = AtMost(3)
52+
val atMostThreeTimes: AtMost = AtMost(3)
53+
val atMostFourTimes: AtMost = AtMost(4)
54+
val atMostFiveTimes: AtMost = AtMost(5)
55+
val atMostSixTimes: AtMost = AtMost(6)
56+
val atMostSevenTimes: AtMost = AtMost(7)
57+
val atMostEightTimes: AtMost = AtMost(8)
58+
val atMostNineTimes: AtMost = AtMost(9)
59+
val atMostTenTimes: AtMost = AtMost(10)
60+
61+
def InOrder(mocks: AnyRef*)(verifications: VerifyInOrder => Verification): Verification = verifications(VerifyInOrder(mocks))
62+
63+
def atLeast(t: Times): AtLeast = AtLeast(t.times)
64+
def atMost(t: Times): AtMost = AtMost(t.times)
65+
66+
implicit class IntOps(i: Int) {
67+
def times: Times = Times(i)
68+
}
69+
70+
}

0 commit comments

Comments
 (0)