สิ่งที่คุณต้องเริ่มต้นคือการเพิ่มการพึ่งพาห้องสมุด MockK
| เข้าใกล้ | คำแนะนำ |
|---|---|
![]() | การทดสอบ "io.mockk: mockk: $ {mockkversion}"
|
(Kotlin DSL) | การทดสอบ ("io.mockk: mockk: $ {mockkversion}") |
![]() | <การพึ่งพา>
<RoupID> io.mockk </roupId>
<ArtIfactId> MOCKK-JVM </ArtIfactId>
<Sersion> $ {MockKversion} </Sersion>
<Scope> ทดสอบ </cope>
</dermentency>
|
การทดสอบ "io.mockk: Mockkk-Android: $ {mockkversion}"
การทดสอบ "io.mockk: mockk-agent: $ {mockkversion}"
| |
AndroidTestimplementation "io.mockk: Mockkk-Android: $ {mockkversion}"
AndroidTestimplementation "io.mockk: Mockk-Agent: $ {Mockkversion}"
|
ตัวอย่างที่ง่ายที่สุด โดยการเยาะเย้ยค่าเริ่มต้นนั้นเข้มงวดดังนั้นคุณต้องให้พฤติกรรมบางอย่าง
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)ดูส่วน "คุณสมบัติ" ด้านล่างสำหรับตัวอย่างรายละเอียดเพิ่มเติม
จากเวอร์ชัน 1.13.0 Mockk รองรับ Kotlin 1.4 และสูงกว่า
mockkStatic อาจไม่ทำงานกับ JDK 16+; InaccessibleObjectException / IllegalAccessException : อ่านเพิ่มเติมที่นี่สารบัญ:
ตรวจสอบชุดของบทความ "การเยาะเย้ยไม่ใช่วิทยาศาสตร์จรวด" ที่ KT Academy อธิบาย Mockk จากพื้นฐานของการเยาะเย้ยถึงคำอธิบายของคุณสมบัติขั้นสูงทั้งหมด
พื้นฐาน
พฤติกรรมที่คาดหวังและการตรวจสอบพฤติกรรม
คุณสมบัติ Mockk
คุณสมบัติขั้นสูงของ Mockk
ทดสอบ Quarkus กับ Kotlin, Junit และ Mockk
คลี่คลายเวทมนตร์สีดำของ Mockk (EN, Translation)
Mockk Guidebook
“ การทดสอบหน่วย Kotlin กับ Mockk” โดย Marco Cattaneo
(วิดีโอ) ใช้การตรวจสอบใน Mockk เพื่อตรวจสอบการเรียกใช้ฟังก์ชันบนวัตถุล้อเลียน
การทดสอบกับ Mockk Pay Course บน raywenderlich.com
TDD สำหรับ Android Video Tutorial ส่วนที่ 1 ตอนที่ 2 โดย Ryan Kay
(วิดีโอ) การเข้ารหัสสดของนักพัฒนา Android #13: การทดสอบหน่วยด้วย Mockk, Coroutines, การพัฒนาแบบทดสอบการพัฒนา
Kotlinconf 2018 - แนวทางปฏิบัติที่ดีที่สุดสำหรับการทดสอบหน่วยใน Kotlin โดย Philipp Hauer
Kotlin-Fullstack-Sample ใช้โครงการ Mockk ที่ครอบคลุมด้วยการทดสอบ
บทความ dzone
บทความ Habrahabr (RU)
เยาะเย้ยใน Kotlin กับ Mockk - Yannick de Turck
คุณสามารถใช้คำอธิบายประกอบเพื่อทำให้การสร้างวัตถุจำลองง่ายขึ้น:
class TrafficSystem {
lateinit var car1 : Car
lateinit var car2 : Car
lateinit var car3 : Car
}
class CarTest {
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@MockK(relaxUnitFun = true )
lateinit var car3 : Car
@SpyK
var car4 = Car ()
@InjectMockKs
var trafficSystem = TrafficSystem ()
@Before
fun setUp () = MockKAnnotations . init ( this , relaxUnitFun = true ) // turn relaxUnitFun on for all mocks
@Test
fun calculateAddsValues1 () {
// ... use car1, car2, car3 and car4
}
} การฉีดครั้งแรกพยายามจับคู่คุณสมบัติตามชื่อจากนั้นโดยชั้นเรียนหรือซูเปอร์คลาส ตรวจสอบพารามิเตอร์ lookupType สำหรับการปรับแต่ง
คุณสมบัติจะถูกฉีดแม้ว่าจะใช้ private ก็ตาม ตัวสร้างสำหรับการฉีดจะถูกเลือกจากจำนวนอาร์กิวเมนต์ที่ใหญ่ที่สุดจนถึงต่ำที่สุด
@InjectMockKs โดยค่าเริ่มต้น injects เฉพาะ lateinit var s หรือ var s ที่ไม่ได้กำหนด หากต้องการเปลี่ยนสิ่งนี้ให้ใช้ overrideValues = true สิ่งนี้จะกำหนดค่าแม้ว่าจะเริ่มต้นแล้วก็ตาม ในการฉีด val ให้ใช้ injectImmutable = true สำหรับสัญกรณ์ที่สั้นกว่าให้ใช้ @OverrideMockKs ซึ่งทำเช่นเดียวกับ @InjectMockKs โดยค่าเริ่มต้น แต่เปิดธงทั้งสองนี้
Junit 4 เปิดเผย API ตามกฎเพื่ออนุญาตให้มีระบบอัตโนมัติบางอย่างหลังจากวงจรการทดสอบ Mockk มีกฎที่ใช้สิ่งนี้เพื่อตั้งค่าและทำลายการเยาะเย้ยของคุณโดยไม่จำเป็นต้องเรียก MockKAnnotations.init(this) ด้วยตนเอง ตัวอย่าง:
class CarTest {
@get:Rule
val mockkRule = MockKRule ( this )
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@Test
fun something () {
every { car1.drive() } just runs
every { car2.changeGear(any()) } returns true
// etc
}
} ใน Junit5 คุณสามารถใช้ MockKExtension เพื่อเริ่มต้นการเยาะเย้ยของคุณ
@ExtendWith( MockKExtension :: class )
class CarTest {
@MockK
lateinit var car1 : Car
@RelaxedMockK
lateinit var car2 : Car
@MockK(relaxUnitFun = true )
lateinit var car3 : Car
@SpyK
var car4 = Car ()
@Test
fun calculateAddsValues1 () {
// ... use car1, car2, car3 and car4
}
} นอกจากนี้ยังเพิ่มความเป็นไปได้ที่จะใช้ @MockK และ @RelaxedMockK บนพารามิเตอร์ฟังก์ชันทดสอบ:
@Test
fun calculateAddsValues1 (@MockK car1 : Car , @RelaxedMockK car2 : Car ) {
// ... use car1 and car2
} ในที่สุดส่วนขยายนี้จะเรียก unmockkAll และ clearAllMocks ในการโทรกลับ @AfterAll เพื่อให้แน่ใจว่าสภาพแวดล้อมการทดสอบของคุณสะอาดหลังจากการดำเนินการทดสอบแต่ละครั้ง คุณสามารถปิดการใช้งานพฤติกรรมนี้ได้โดยการเพิ่ม @MockKExtension.KeepMocks คำอธิบายประกอบเข้ากับชั้นเรียนของคุณหรือทั่วโลกโดยการตั้งค่า mockk.junit.extension.keepmocks=true (เนื่องจาก v1.13.11) อีกทางเลือกหนึ่งเนื่องจาก clearAllMocks โดยค่าเริ่มต้น ( currentThreadOnly=false mockk.junit.extension.requireParallelTesting=true ไม่ปลอดภัยหากคุณต้องการเรียกใช้การทดสอบในคู่ขนานคุณ @AfterAll เพิ่ม MockKExtension.RequireParallelTesting หากมีการเรียก clearAllMocks อย่างชัดเจนคุณสามารถจัดหา clearAllMocks(currentThreadOnly = true) เพื่อที่จะล้าง mocks ที่สร้างขึ้นภายในเธรดเดียวกันเท่านั้น (ตั้งแต่ V1.13.12)
คุณสามารถตรวจสอบให้แน่ใจว่าวิธีการ stubbed ทั้งหมดได้รับการตรวจสอบจริงโดยการใส่คำอธิบายประกอบคลาสทดสอบของคุณด้วย @MockKExtension.ConfirmVerification
สิ่งนี้จะเรียกว่า confirmVerified ภายในทุกครั้งหลังจากการทดสอบแต่ละครั้งเพื่อให้แน่ใจว่าไม่มีสตับบิ๊กที่ไม่จำเป็น
โปรดทราบว่าพฤติกรรมนี้อาจไม่ทำงานตามที่คาดไว้เมื่อทำการทดสอบใน IDE ของคุณเนื่องจากเป็น Gradle ที่ดูแลการจัดการข้อยกเว้นที่ถูกโยนทิ้งเมื่อการโทร confirmVerified เหล่านี้ล้มเหลว
คุณสามารถตรวจสอบให้แน่ใจว่าวิธีการ stubbed ทั้งหมดมีประโยชน์ - ใช้อย่างน้อยหนึ่งครั้ง - โดยการใส่คำอธิบายประกอบคลาสการทดสอบของคุณด้วย @MockKExtension.CheckUnnecessaryStub
สิ่งนี้จะเรียก checkUnnecessaryStub ภายในทุกครั้งหลังจากการทดสอบแต่ละครั้งเพื่อให้แน่ใจว่าไม่มีสตับบิ๊กที่ไม่จำเป็น
สายลับช่วยให้คุณผสมผสานการเยาะเย้ยและวัตถุจริง
val car = spyk( Car ()) // or spyk<Car>() to call the default constructor
car.drive( Direction . NORTH ) // returns whatever the real function of Car returns
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)หมายเหตุ 1: วัตถุสายลับเป็นสำเนาของวัตถุที่ผ่าน หมายเหตุ 2: มีปัญหาที่ทราบหากใช้สายลับกับฟังก์ชั่นการระงับ: #554
relaxed mock คือการเยาะเย้ยที่ส่งคืนค่าง่าย ๆ สำหรับฟังก์ชั่นทั้งหมด สิ่งนี้ช่วยให้คุณข้ามพฤติกรรมที่ระบุสำหรับแต่ละกรณีในขณะที่ยังคงทำสิ่งที่คุณต้องการ สำหรับประเภทการอ้างอิงการจำลองที่ถูกล่ามโซ่จะถูกส่งคืน
val car = mockk< Car >(relaxed = true )
car.drive( Direction . NORTH ) // returns null
verify { car.drive( Direction . NORTH ) }
confirmVerified(car)หมายเหตุ: การเยาะเย้ยที่ผ่อนคลายนั้นทำงานได้ไม่ดีกับประเภทผลตอบแทนทั่วไป ข้อยกเว้นในชั้นเรียนมักจะถูกโยนลงไปในกรณีนี้ เลือกใช้ Stubbing ด้วยตนเองในกรณีของประเภทการส่งคืนทั่วไป
วิธีแก้ปัญหา:
val func = mockk < () -> Car > (relaxed = true ) // in this case invoke function has generic return type
// this line is workaround, without it the relaxed mock would throw a class cast exception on the next line
every { func() } returns Car () // or you can return mockk() for example
func() บางครั้งคุณจำเป็นต้องพูดถึงฟังก์ชั่นบางอย่าง แต่ก็ยังเรียกวิธีการจริงกับผู้อื่นหรือในข้อโต้แย้งที่เฉพาะเจาะจง สิ่งนี้เป็นไปได้โดยผ่าน callOriginal() ไปยัง answers ซึ่งใช้ได้ผลสำหรับการเยาะเย้ยทั้งแบบผ่อนคลายและไม่ผ่อนคลาย
class Adder {
fun addOne ( num : Int ) = num + 1
}
val adder = mockk< Adder >()
every { adder.addOne(any()) } returns - 1
every { adder.addOne( 3 ) } answers { callOriginal() }
assertEquals( - 1 , adder.addOne( 2 ))
assertEquals( 4 , adder.addOne( 3 )) // original function is called หากคุณต้องการให้ Unit -ฟังก์ชั่นการกลับมาผ่อนคลายคุณสามารถใช้ relaxUnitFun = true เป็นอาร์กิวเมนต์ของฟังก์ชั่น mockk , @MockK Annotation หรือ MockKAnnotations.init ฟังก์ชั่น
การทำงาน:
mockk< ClassBeingMocked >(relaxUnitFun = true )คำอธิบายประกอบ:
@MockK(relaxUnitFun = true )
lateinit var mock1 : ClassBeingMocked
init {
MockKAnnotations . init ( this )
}mockkannotations.init:
@MockK
lateinit var mock2 : ClassBeingMocked
init {
MockKAnnotations . init ( this , relaxUnitFun = true )
}วัตถุสามารถเปลี่ยนเป็น mocks ด้วยวิธีต่อไปนี้:
object ObjBeingMocked {
fun add ( a : Int , b : Int ) = a + b
}
mockkObject( ObjBeingMocked ) // applies mocking to an Object
assertEquals( 3 , ObjBeingMocked .add( 1 , 2 ))
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 )) หากต้องการย้อนกลับกลับไปใช้ unmockkObject หรือ unmockkAll (การทำลายล้างมากขึ้น: ยกเลิกวัตถุ, การจำลองแบบคงที่และคอนสตรัคเตอร์)
@Before
fun beforeTests () {
mockkObject( ObjBeingMocked )
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
}
@Test
fun willUseMockBehaviour () {
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 ))
}
@After
fun afterTests () {
unmockkObject( ObjBeingMocked )
// or unmockkAll()
}แม้จะมีข้อ จำกัด ด้านภาษา Kotlin คุณสามารถสร้างอินสแตนซ์ของวัตถุใหม่ได้หากจำเป็นโดยการทดสอบตรรกะ:
val newObjectMock = mockk< ObjBeingMocked >() บางครั้งคุณต้องการการจำลองของชั้นเรียนโดยพลการ ใช้ mockkClass ในกรณีเหล่านั้น
val car = mockkClass( Car :: class )
every { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
verify { car.drive( Direction . NORTH ) } enums สามารถล้อเลียนโดยใช้ mockkObject :
enum class Enumeration ( val goodInt : Int ) {
CONSTANT ( 35 ),
OTHER_CONSTANT ( 45 );
}
mockkObject( Enumeration . CONSTANT )
every { Enumeration . CONSTANT .goodInt } returns 42
assertEquals( 42 , Enumeration . CONSTANT .goodInt)บางครั้งโดยเฉพาะอย่างยิ่งในรหัสที่คุณไม่ได้เป็นเจ้าของคุณต้องเยาะเย้ยวัตถุที่สร้างขึ้นใหม่ เพื่อจุดประสงค์นี้มีการจัดทำสิ่งก่อสร้างต่อไปนี้:
class MockCls {
fun add ( a : Int , b : Int ) = a + b
}
mockkConstructor( MockCls :: class )
every { anyConstructed< MockCls >().add( 1 , 2 ) } returns 4
assertEquals( 4 , MockCls ().add( 1 , 2 )) // note new object is created
verify { anyConstructed< MockCls >().add( 1 , 2 ) } แนวคิดพื้นฐานคือหลังจากตัวสร้างของคลาสที่ล้อเลียนถูกดำเนินการ (ใด ๆ ) วัตถุกลายเป็น constructed mock
พฤติกรรมการเยาะเย้ยของการเยาะเย้ยดังกล่าวเชื่อมต่อกับ prototype mock พิเศษที่แสดงโดย anyConstructed<MockCls>()
มีหนึ่งอินสแตนซ์ต่อคลาสของ prototype mock การบันทึกการโทรยังเกิดขึ้นกับ prototype mock
หากไม่มีการระบุพฤติกรรมสำหรับฟังก์ชั่นฟังก์ชันดั้งเดิมจะถูกดำเนินการ
ในกรณีที่ชั้นเรียนมีตัวสร้างมากกว่าหนึ่งตัวแต่ละตัวสามารถล้อเลียนแยกกันได้:
class MockCls ( private val a : Int = 0 ) {
constructor (x : String ) : this (x.toInt())
fun add ( b : Int ) = a + b
}
mockkConstructor( MockCls :: class )
every { constructedWith< MockCls >().add( 1 ) } returns 2
every {
constructedWith< MockCls >( OfTypeMatcher < String >( String :: class )).add( 2 ) // Mocks the constructor which takes a String
} returns 3
every {
constructedWith< MockCls >( EqMatcher ( 4 )).add(any()) // Mocks the constructor which takes an Int
} returns 4
assertEquals( 2 , MockCls ().add( 1 ))
assertEquals( 3 , MockCls ( " 2 " ).add( 2 ))
assertEquals( 4 , MockCls ( 4 ).add( 7 ))
verify {
constructedWith< MockCls >().add( 1 )
constructedWith< MockCls >( " 2 " ).add( 2 )
constructedWith< MockCls >( EqMatcher ( 4 )).add( 7 )
} โปรดทราบว่าในกรณีนี้มีการสร้าง prototype mock สำหรับทุกชุดของชุดจับคู่อาร์กิวเมนต์ที่ส่งผ่านไปยัง constructedWith
คุณสามารถผสมทั้งอาร์กิวเมนต์และจับคู่:
val car = mockk< Car >()
every {
car.recordTelemetry(
speed = more( 50 ),
direction = Direction . NORTH , // here eq() is used
lat = any(),
long = any()
)
} returns Outcome . RECORDED
car.recordTelemetry( 60 , Direction . NORTH , 51.1377382 , 17.0257142 )
verify { car.recordTelemetry( 60 , Direction . NORTH , 51.1377382 , 17.0257142 ) }
confirmVerified(car)คุณสามารถสายโซ่ของสาย:
val car = mockk< Car >()
every { car.door( DoorType . FRONT_LEFT ).windowState() } returns WindowState . UP
car.door( DoorType . FRONT_LEFT ) // returns chained mock for Door
car.door( DoorType . FRONT_LEFT ).windowState() // returns WindowState.UP
verify { car.door( DoorType . FRONT_LEFT ).windowState() }
confirmVerified(car) หมายเหตุ: หากประเภทการส่งคืนของฟังก์ชั่นเป็นข้อมูลทั่วไปข้อมูลเกี่ยวกับประเภทจริงจะหายไป
เพื่อให้การโทรถูกล่ามโซ่จำเป็นต้องใช้ข้อมูลเพิ่มเติม
ส่วนใหญ่เฟรมเวิร์กจะจับข้อยกเว้นและทำ autohinting
ในกรณีที่จำเป็นอย่างชัดเจนให้ใช้ hint ก่อนโทรครั้งต่อไป
every { obj.op2( 1 , 2 ).hint( Int :: class ).op1( 3 , 4 ) } returns 5จากรุ่น 1.9.1 mocks อาจถูกล่ามโซ่ไว้ในลำดับชั้น:
interface AddressBook {
val contacts : List < Contact >
}
interface Contact {
val name : String
val telephone : String
val address : Address
}
interface Address {
val city : String
val zip : String
}
val addressBook = mockk< AddressBook > {
every { contacts } returns listOf (
mockk {
every { name } returns " John "
every { telephone } returns " 123-456-789 "
every { address.city } returns " New-York "
every { address.zip } returns " 123-45 "
},
mockk {
every { name } returns " Alex "
every { telephone } returns " 789-456-123 "
every { address } returns mockk {
every { city } returns " Wroclaw "
every { zip } returns " 543-21 "
}
}
)
} คุณสามารถจับอาร์กิวเมนต์ไปยัง CapturingSlot หรือ MutableList
CapturingSlot มักจะถูกสร้างขึ้นผ่าน slot<T : Any?>() และเป็นไปได้ที่จะจับภาพที่ไม่มีค่าใช้จ่ายและไม่เป็นโมฆะ MutableList มีไว้สำหรับการจับค่าหลายค่าระหว่างการทดสอบ
enum class Direction { NORTH , SOUTH }
enum class RecordingOutcome { RECORDED }
enum class RoadType { HIGHWAY }
class Car {
fun recordTelemetry ( speed : Double , direction : Direction , roadType : RoadType ? ): RecordingOutcome {
TODO ( " not implement for showcase " )
}
}
val car = mockk< Car >()
// allow to capture parameter with non nullable type `Double`
val speedSlot = slot< Double >()
// allow to capture parameter with nullable type `RoadType`
val roadTypeSlot = slot< RoadType ?>()
val list = mutableListOf< Double >()
every {
car.recordTelemetry(
speed = capture(speedSlot), // makes mock match calls with any value for `speed` and record it in a slot
direction = Direction . NORTH , // makes mock and capturing only match calls with specific `direction`. Use `any()` to match calls with any `direction`
roadType = captureNullable(roadTypeSlot), // makes mock match calls with any value for `roadType` and record it in a slot
)
} answers {
println ( " Speed: ${speedSlot.captured} , roadType: ${roadTypeSlot.captured} " )
RecordingOutcome . RECORDED
}
every {
car.recordTelemetry(
speed = capture(list),
direction = Direction . SOUTH ,
roadType = captureNullable(roadTypeSlot),
)
} answers {
println ( " Speed: ${list} , roadType: ${roadTypeSlot.captured} " )
RecordingOutcome . RECORDED
}
car.recordTelemetry(speed = 15.0 , direction = Direction . NORTH , null ) // prints Speed: 15.0, roadType: null
car.recordTelemetry(speed = 16.0 , direction = Direction . SOUTH , RoadType . HIGHWAY ) // prints Speed: [16.0], roadType: HIGHWAY
verifyOrder {
car.recordTelemetry(speed = or ( 15.0 , 16.0 ), direction = any(), roadType = null )
car.recordTelemetry(speed = 16.0 , direction = any(), roadType = RoadType . HIGHWAY )
}
confirmVerified(car) คุณสามารถตรวจสอบจำนวนการโทรด้วย atLeast , atMost หรือ exactly :
val car = mockk< Car >(relaxed = true )
car.accelerate(fromSpeed = 10 , toSpeed = 20 )
car.accelerate(fromSpeed = 10 , toSpeed = 30 )
car.accelerate(fromSpeed = 20 , toSpeed = 30 )
// all pass
verify(atLeast = 3 ) { car.accelerate(allAny()) }
verify(atMost = 2 ) { car.accelerate(fromSpeed = 10 , toSpeed = or ( 20 , 30 )) }
verify(exactly = 1 ) { car.accelerate(fromSpeed = 10 , toSpeed = 20 ) }
verify(exactly = 0 ) { car.accelerate(fromSpeed = 30 , toSpeed = 10 ) } // means no calls were performed
confirmVerified(car) หรือคุณสามารถใช้ verifyCount :
val car = mockk< Car >(relaxed = true )
car.accelerate(fromSpeed = 10 , toSpeed = 20 )
car.accelerate(fromSpeed = 10 , toSpeed = 30 )
car.accelerate(fromSpeed = 20 , toSpeed = 30 )
// all pass
verifyCount {
( 3 .. 5 ) * { car.accelerate(allAny(), allAny()) } // same as verify(atLeast = 3, atMost = 5) { car.accelerate(allAny(), allAny()) }
1 * { car.accelerate(fromSpeed = 10 , toSpeed = 20 ) } // same as verify(exactly = 1) { car.accelerate(fromSpeed = 10, toSpeed = 20) }
0 * { car.accelerate(fromSpeed = 30 , toSpeed = 10 ) } // same as verify(exactly = 0) { car.accelerate(fromSpeed = 30, toSpeed = 10) }
}
confirmVerified(car)verifyAll ตรวจสอบว่าการโทรทั้งหมดเกิดขึ้นโดยไม่ตรวจสอบคำสั่งซื้อของพวกเขาverifySequence ตรวจสอบว่าการโทรเกิดขึ้นในลำดับที่ระบุverifyOrder ตรวจสอบว่าการโทรเกิดขึ้นในลำดับที่เฉพาะเจาะจงwasNot Called การตรวจสอบว่าการเยาะเย้ย (หรือรายการของการเยาะเย้ย) ไม่ได้ถูกเรียกเลย class MockedClass {
fun sum ( a : Int , b : Int ) = a + b
}
val obj = mockk< MockedClass >()
val slot = slot< Int >()
every {
obj.sum(any(), capture(slot))
} answers {
1 + firstArg< Int >() + slot.captured
}
obj.sum( 1 , 2 ) // returns 4
obj.sum( 1 , 3 ) // returns 5
obj.sum( 2 , 2 ) // returns 5
verifyAll {
obj.sum( 1 , 3 )
obj.sum( 1 , 2 )
obj.sum( 2 , 2 )
}
verifySequence {
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
obj.sum( 2 , 2 )
}
verifyOrder {
obj.sum( 1 , 2 )
obj.sum( 2 , 2 )
}
val obj2 = mockk< MockedClass >()
val obj3 = mockk< MockedClass >()
verify {
listOf (obj2, obj3) wasNot Called
}
confirmVerified(obj) หากต้องการตรวจสอบอีกครั้งว่าการโทรทั้งหมดได้รับการตรวจสอบโดย verify... สร้างคุณสามารถใช้ confirmVerified :
confirmVerified(mock1, mock2) มันไม่สมเหตุสมผลเลยที่จะใช้สำหรับ verifySequence และ verifyAll เนื่องจากวิธีการตรวจสอบเหล่านี้ครอบคลุมการโทรทั้งหมดอย่างละเอียดพร้อมการตรวจสอบ
มันจะโยนข้อยกเว้นหากมีการโทรเหลืออยู่โดยไม่มีการตรวจสอบ
การโทรบางอย่างสามารถแยกออกจากการยืนยันนี้ตรวจสอบส่วนถัดไปสำหรับรายละเอียดเพิ่มเติม
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
every { car.drive( Direction . SOUTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
car.drive( Direction . SOUTH ) // returns OK
verify {
car.drive( Direction . SOUTH )
car.drive( Direction . NORTH )
}
confirmVerified(car) // makes sure all calls were covered with verificationเนื่องจากรหัสทดสอบที่สะอาดและบำรุงรักษาต้องใช้รหัสที่ไม่จำเป็นเป็นศูนย์คุณสามารถมั่นใจได้ว่าไม่มีสตับที่ไม่จำเป็น
checkUnnecessaryStub(mock1, mock2)มันจะโยนข้อยกเว้นหากมีการโทรบางอย่างที่ประกาศเกี่ยวกับการเยาะเย้ยที่ไม่ได้ใช้โดยรหัสที่ทดสอบ สิ่งนี้สามารถเกิดขึ้นได้หากคุณได้ประกาศสตับที่ไม่จำเป็นจริง ๆ หรือหากรหัสที่ทดสอบไม่เรียกว่าอันที่คาดหวัง
ในการยกเว้นการโทรที่ไม่สำคัญจากการถูกบันทึกคุณสามารถใช้ excludeRecords :
excludeRecords { mock.operation(any(), 5 ) } การโทรที่ตรงกันทั้งหมดจะถูกแยกออกจากการบันทึก สิ่งนี้อาจมีประโยชน์หากคุณใช้การตรวจสอบอย่างละเอียด: verifyAll , verifySequence หรือ confirmVerified
val car = mockk< Car >()
every { car.drive( Direction . NORTH ) } returns Outcome . OK
every { car.drive( Direction . SOUTH ) } returns Outcome . OK
excludeRecords { car.drive( Direction . SOUTH ) }
car.drive( Direction . NORTH ) // returns OK
car.drive( Direction . SOUTH ) // returns OK
verify {
car.drive( Direction . NORTH )
}
confirmVerified(car) // car.drive(Direction.SOUTH) was excluded, so confirmation is fine with only car.drive(Direction.NORTH) ในการตรวจสอบการดำเนินการพร้อมกันคุณสามารถใช้ timeout = xxx :
mockk< MockCls > {
every { sum( 1 , 2 ) } returns 4
Thread {
Thread .sleep( 2000 )
sum( 1 , 2 )
}.start()
verify(timeout = 3000 ) { sum( 1 , 2 ) }
}สิ่งนี้จะรอจนกว่าหนึ่งในสองสถานะต่อไปนี้: การตรวจสอบจะผ่านหรือหมดเวลา
หากฟังก์ชั่นส่งคืน Unit คุณสามารถใช้โครงสร้าง justRun :
class MockedClass {
fun sum ( a : Int , b : Int ): Unit {
println (a + b)
}
}
val obj = mockk< MockedClass >()
justRun { obj.sum(any(), 3 ) }
obj.sum( 1 , 1 )
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
verify {
obj.sum( 1 , 1 )
obj.sum( 1 , 2 )
obj.sum( 1 , 3 )
} วิธีอื่น ๆ ในการเขียน justRun { obj.sum(any(), 3) } :
every { obj.sum(any(), 3) } just Runsevery { obj.sum(any(), 3) } returns Unitevery { obj.sum(any(), 3) } answers { Unit }ในการเยาะเย้ย coroutines คุณต้องเพิ่มการพึ่งพาอีกครั้งในห้องสมุดสนับสนุน
| ผู้สำเร็จการศึกษา |
|---|
การทดสอบ "org.jetbrains.kotlinx: kotlinx-coroutines-core: xx" |
| หนอง |
|---|
<การพึ่งพา>
<RoupID> org.jetbrains.kotlinx </groupId>
<ArtIfactId> kotlinx-coroutines-core </artifactid>
<Sersion> XX </Sersion>
<Scope> ทดสอบ </cope>
</dermentency> |
จากนั้นคุณสามารถใช้ coEvery , coVerify , coMatch , coAssert , coRun , coAnswers หรือ coInvoke เพื่อจำลองฟังก์ชั่นระงับ
val car = mockk< Car >()
coEvery { car.drive( Direction . NORTH ) } returns Outcome . OK
car.drive( Direction . NORTH ) // returns OK
coVerify { car.drive( Direction . NORTH ) } และเพื่อจำลองฟังก์ชั่น suspend ที่ไม่เคยส่งคืนคุณสามารถใช้ coJustAwait :
runTest {
val car = mockk< Car >()
coJustAwait { car.drive(any()) } // car.drive(...) will never return
val job = launch( UnconfinedTestDispatcher ()) {
car.drive( Direction . NORTH )
}
coVerify { car.drive( Direction . NORTH ) }
job.cancelAndJoin() // Don't forget to cancel the job
}หมายเหตุ: มีปัญหาที่ทราบหากใช้สายลับกับฟังก์ชั่นระงับ: #554
Kotlin ให้คุณประกาศฟังก์ชั่นที่ไม่ได้อยู่ในคลาสหรือวัตถุใด ๆ ที่เรียกว่าฟังก์ชั่นระดับบนสุด การโทรเหล่านี้ถูกแปลเป็นวิธีการคงที่ในสภาพแวดล้อม jvm และคลาส Java พิเศษถูกสร้างขึ้นเพื่อเก็บฟังก์ชั่น ฟังก์ชั่นระดับบนสุดเหล่านี้สามารถล้อเลียนโดยใช้ mockkStatic คุณเพียงแค่ต้องนำเข้าฟังก์ชั่นและส่งข้อมูลอ้างอิงเป็นอาร์กิวเมนต์:
import com.cars.buildCar
val testCar = Car ()
mockkStatic(::buildCar)
every { buildCar() } returns testCar
assertEquals(testCar, buildCar())
verify { buildCar() } การเยาะเย้ยฟังก์ชั่นจะล้างการเยาะเย้ยที่มีอยู่ของฟังก์ชั่นอื่น ๆ ที่ประกาศไว้ในไฟล์เดียวกันซึ่งเทียบเท่ากับการเรียก clearStaticMockk บนคลาสที่สร้างขึ้น
มีฟังก์ชั่นการขยายสามประเภทใน Kotlin:
สำหรับวัตถุหรือคลาสคุณสามารถเยาะเย้ยฟังก์ชั่นส่วนขยายได้เพียงแค่สร้าง mockk ปกติ:
data class Obj ( val value : Int )
class Ext {
fun Obj. extensionFunc () = value + 5
}
with (mockk< Ext >()) {
every {
Obj ( 5 ).extensionFunc()
} returns 11
assertEquals( 11 , Obj ( 5 ).extensionFunc())
verify {
Obj ( 5 ).extensionFunc()
}
} ในการเยาะเย้ยฟังก์ชั่นส่วนขยายกว้างโมดูลคุณต้องสร้าง mockkStatic(...) ด้วยชื่อคลาสของโมดูลเป็นอาร์กิวเมนต์ ตัวอย่างเช่น "pkg.filekt" สำหรับ module File.kt ในแพ็คเกจ pkg
data class Obj ( val value : Int )
// declared in File.kt ("pkg" package)
fun Obj. extensionFunc () = value + 5
mockkStatic( " pkg.FileKt " )
every {
Obj ( 5 ).extensionFunc()
} returns 11
assertEquals( 11 , Obj ( 5 ).extensionFunc())
verify {
Obj ( 5 ).extensionFunc()
} ในสภาพแวดล้อม jvm คุณสามารถแทนที่ชื่อคลาสด้วยการอ้างอิงฟังก์ชั่น:
mockkStatic( Obj ::extensionFunc) โปรดทราบว่าสิ่งนี้จะเยาะเย้ยคลาส pkg.FileKt ทั้งหมดและไม่ใช่แค่ extensionFunc
ไวยากรณ์นี้ยังใช้สำหรับคุณสมบัติการขยาย:
val Obj .squareValue get() = value * value
mockkStatic( Obj ::squareValue) หากใช้ @JvmName ให้ระบุว่าเป็นชื่อคลาส
khttp.kt:
@file:JvmName( " KHttp " )
package khttp
// ... KHttp code รหัสทดสอบ:
mockkStatic( " khttp.KHttp " ) บางครั้งคุณต้องรู้เพิ่มเติมเล็กน้อยเพื่อเยาะเย้ยฟังก์ชั่นส่วนขยาย ตัวอย่างเช่นไฟล์ฟังก์ชันส่วนขยาย File.endsWith() มี classname ที่คาดเดาไม่ได้ทั้งหมด:
mockkStatic( " kotlin.io.FilesKt__UtilsKt " )
every { File ( " abc " ).endsWith(any< String >()) } returns true
println ( File ( " abc " ).endsWith( " abc " )) นี่คือพฤติกรรม Kotlin มาตรฐานที่อาจคาดเดาไม่ได้ ใช้ Tools -> Kotlin -> Show Kotlin Bytecode หรือตรวจสอบไฟล์ .class ใน Jar Archive เพื่อตรวจจับชื่อดังกล่าว
จากเวอร์ชัน 1.9.1 การจัดการ Vararg ที่ขยายเพิ่มเติมเป็นไปได้:
interface ClsWithManyMany {
fun manyMany ( vararg x : Any ): Int
}
val obj = mockk< ClsWithManyMany >()
every { obj.manyMany( 5 , 6 , * varargAll { it == 7 }) } returns 3
println (obj.manyMany( 5 , 6 , 7 )) // 3
println (obj.manyMany( 5 , 6 , 7 , 7 )) // 3
println (obj.manyMany( 5 , 6 , 7 , 7 , 7 )) // 3
every { obj.manyMany( 5 , 6 , * anyVararg(), 7 ) } returns 4
println (obj.manyMany( 5 , 6 , 1 , 7 )) // 4
println (obj.manyMany( 5 , 6 , 2 , 3 , 7 )) // 4
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 )) // 4
every { obj.manyMany( 5 , 6 , * varargAny { nArgs > 5 }, 7 ) } returns 5
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 )) // 5
println (obj.manyMany( 5 , 6 , 4 , 5 , 6 , 7 , 7 )) // 5
every {
obj.manyMany( 5 , 6 , * varargAny {
if (position < 3 ) it == 3 else it == 4
}, 7 )
} returns 6
println (obj.manyMany( 5 , 6 , 3 , 4 , 7 )) // 6
println (obj.manyMany( 5 , 6 , 3 , 4 , 4 , 7 )) // 6หากคุณต้องการเยาะเย้ยฟังก์ชั่นส่วนตัวคุณสามารถทำได้ผ่านการโทรแบบไดนามิก
class Car {
fun drive () = accelerate()
private fun accelerate () = " going faster "
}
val mock = spyk< Car >(recordPrivateCalls = true )
every { mock[ " accelerate " ]() } returns " going not so fast "
assertEquals( " going not so fast " , mock.drive())
verifySequence {
mock.drive()
mock[ " accelerate " ]()
} หากคุณต้องการตรวจสอบการโทรส่วนตัวคุณควรสร้าง spyk ด้วย recordPrivateCalls = true
นอกจากนี้ไวยากรณ์ verbose มากขึ้นช่วยให้คุณได้รับและตั้งค่าคุณสมบัติรวมกับการโทรแบบไดนามิกเดียวกัน:
val mock = spyk( Team (), recordPrivateCalls = true )
every { mock getProperty " speed " } returns 33
every { mock setProperty " acceleration " value less( 5 ) } just runs
justRun { mock invokeNoArgs " privateMethod " }
every { mock invoke " openDoor " withArguments listOf ( " left " , " rear " ) } returns " OK "
verify { mock getProperty " speed " }
verify { mock setProperty " acceleration " value less( 5 ) }
verify { mock invoke " openDoor " withArguments listOf ( " left " , " rear " ) } คุณสามารถเข้าถึงฟิลด์สำรองผ่าน fieldValue และใช้ value สำหรับค่าที่ตั้งไว้
หมายเหตุ: ในตัวอย่างด้านล่างเราใช้ propertyType เพื่อระบุประเภทของ fieldValue สิ่งนี้จำเป็นเพราะเป็นไปได้ที่จะจับประเภทโดยอัตโนมัติสำหรับ Getter ใช้ nullablePropertyType เพื่อระบุประเภทที่ไม่มีค่าใช้จ่าย
val mock = spyk( MockCls (), recordPrivateCalls = true )
every { mock.property } answers { fieldValue + 6 }
every { mock.property = any() } propertyType Int :: class answers { fieldValue + = value }
every { mock getProperty " property " } propertyType Int :: class answers { fieldValue + 6 }
every { mock setProperty " property " value any< Int >() } propertyType Int :: class answers { fieldValue + = value }
every {
mock.property = any()
} propertyType Int :: class answers {
fieldValue = value + 1
} andThen {
fieldValue = value - 1
}การเพิ่มพฤติกรรมเพิ่มเติมผ่านอินเทอร์เฟซและทำให้พวกเขา:
val spy = spyk( System . out , moreInterfaces = arrayOf( Runnable :: class ))
spy. println ( 555 )
every {
(spy as Runnable ). run ()
} answers {
(self as PrintStream ). println ( " Run! Run! Run! " )
}
val thread = Thread (spy as Runnable )
thread.start()
thread.join() ไม่มีอะไรพิเศษที่นี่ หากคุณมีฟังก์ชั่น Nothing ที่ส่งคืน:
fun quit ( status : Int ): Nothing {
exitProcess(status)
}จากนั้นคุณสามารถเช่นโยนข้อยกเว้นเป็นพฤติกรรม:
every { quit( 1 ) } throws Exception ( " this is a test " ) การจำลองที่มีการกำหนดขอบเขตเป็นล้อเลียนที่ปลดปล่อยตัวเองโดยอัตโนมัติหลังจากบล็อกรหัสผ่านเมื่อพารามิเตอร์ถูกดำเนินการแล้ว คุณสามารถใช้ฟังก์ชั่น mockkObject , mockkStatic และ mockkConstructor
object ObjBeingMocked {
fun add ( a : Int , b : Int ) = a + b
}
// ObjBeingMocked will be unmocked after this scope
mockkObject( ObjBeingMocked ) {
assertEquals( 3 , ObjBeingMocked .add( 1 , 2 ))
every { ObjBeingMocked .add( 1 , 2 ) } returns 55
assertEquals( 55 , ObjBeingMocked .add( 1 , 2 ))
} วิธีที่ง่ายมากในการสร้าง matchers ใหม่คือการแนบฟังก์ชั่นกับ MockKMatcherScope หรือ MockKVerificationScope และใช้ฟังก์ชั่นการ match :
fun MockKMatcherScope. seqEq ( seq : Sequence < String >) = match< Sequence < String >> {
it.toList() == seq.toList()
} นอกจากนี้ยังเป็นไปได้ที่จะสร้าง matchers ขั้นสูงมากขึ้นโดยใช้อินเตอร์เฟส Matcher
ตัวอย่างของตัวจับคู่แบบกำหนดเองที่เปรียบเทียบรายการโดยไม่มีการสั่งซื้อ:
@Test
fun test () {
class MockCls {
fun op ( a : List < Int >) = a.reversed()
}
val mock = mockk< MockCls >()
every { mock.op(any()) } returns listOf ( 5 , 6 , 9 )
println (mock.op( listOf ( 1 , 2 , 3 )))
verify { mock.op(matchListWithoutOrder( 3 , 2 , 1 )) }
}
data class ListWithoutOrderMatcher < T >(
val expectedList : List < T >,
val refEq : Boolean
) : Matcher<List<T>> {
val map = buildCountsMap(expectedList, refEq)
override fun match ( arg : List < T > ? ): Boolean {
if (arg == null ) return false
return buildCountsMap(arg, refEq) == map
}
private fun buildCountsMap ( list : List < T >, ref : Boolean ): Map < Any ?, Int > {
val map = mutableMapOf< Any ?, Int >()
for (item in list) {
val key = when {
item == null -> nullKey
refEq -> InternalPlatform .ref(item)
else -> item
}
map.compute(key, { _, value -> (value ? : 0 ) + 1 })
}
return map
}
override fun toString () = " matchListWithoutOrder( $expectedList ) "
@Suppress( " UNCHECKED_CAST " )
override fun substitute ( map : Map < Any , Any >): Matcher < List < T >> {
return copy(expectedList = expectedList.map { map.getOrDefault(it as Any? , it) } as List < T >)
}
companion object {
val nullKey = Any ()
}
}
inline fun < reified T : List < E >, E : Any > MockKMatcherScope. matchListWithoutOrder (
vararg items : E ,
refEq : Boolean = true
): T = match( ListWithoutOrderMatcher ( listOf ( * items), refEq))ตัวอย่างการใช้การสะท้อนกลับเพื่อเยาะเย้ยทุกวิธีในวัตถุสไตล์ตัวสร้าง
val builderFunctions = MyBuilder :: class .memberFunctions.filter { it.returnType.classifier == MyBuilder :: class }
val builderMock = mockk< MyBuilder > {
builderFunctions.forEach { func ->
every {
val params = listOf< Any ?>(builderMock) + func.parameters.drop( 1 ).map { any(it.type.classifier as KClass < Any >) }
func.call( * params.toTypedArray())
} answers {
this @mockk
}
}
}ในการปรับพารามิเตอร์ทั่วโลกมีการตั้งค่าบางอย่างที่คุณสามารถระบุได้ในไฟล์ทรัพยากร
วิธีใช้:
io/mockk/settings.properties ใน src/main/resources relaxed =true|false
relaxUnitFun =true|false
recordPrivateCalls =true|false
stackTracesOnVerify =true|false
stackTracesAlignment =left|center stackTracesAlignment กำหนดว่าจะจัดเรียงร่องรอยสแต็กให้เข้ากับกึ่งกลาง (ค่าเริ่มต้น) หรือไปทางซ้าย (สอดคล้องกับ JVM stacktraces ตามปกติ)
นี่คือตารางสองสามอันที่จะช่วยให้คุณเชี่ยวชาญ DSL
| การทำงาน | คำอธิบาย |
|---|---|
mockk<T>(...) | สร้างเยาะเย้ยเป็นประจำ |
spyk<T>() | สร้างสายลับโดยใช้ตัวสร้างเริ่มต้น |
spyk(obj) | สร้างสายลับโดยการคัดลอกจาก obj |
slot | สร้างสล็อตจับภาพ |
every | เริ่มบล็อกตบเบา ๆ |
coEvery | เริ่มต้นบล็อก stubbing สำหรับ coroutines |
verify | เริ่มบล็อกการตรวจสอบ |
coVerify | เริ่มบล็อกการตรวจสอบสำหรับ coroutines |
verifyAll | เริ่มบล็อกการตรวจสอบที่ควรรวมการโทรทั้งหมด |
coVerifyAll | เริ่มบล็อกการตรวจสอบที่ควรรวมการเรียกทั้งหมดสำหรับ coroutines |
verifyOrder | เริ่มบล็อกการตรวจสอบที่ตรวจสอบคำสั่งซื้อ |
coVerifyOrder | เริ่มบล็อกการตรวจสอบที่ตรวจสอบคำสั่งสำหรับ coroutines |
verifySequence | เริ่มบล็อกการตรวจสอบที่ตรวจสอบว่ามีการโทรทั้งหมดในลำดับที่ระบุ |
coVerifySequence | เริ่มบล็อกการตรวจสอบที่ตรวจสอบว่ามีการโทรทั้งหมดในลำดับที่ระบุสำหรับ coroutines |
excludeRecords | ไม่รวมการโทรบางส่วนจากการบันทึก |
confirmVerified | ยืนยันว่าการโทรที่บันทึกทั้งหมดได้รับการยืนยันแล้ว |
checkUnnecessaryStub | ยืนยันว่าการโทรที่บันทึกทั้งหมดใช้อย่างน้อยหนึ่งครั้ง |
clearMocks | ล้างการจำลองที่ระบุ |
registerInstanceFactory | ช่วยให้คุณกำหนดวิธีการสร้างอินสแตนซ์สำหรับวัตถุบางอย่างใหม่ |
mockkClass | สร้างจำลองปกติโดยผ่านชั้นเรียนเป็นพารามิเตอร์ |
mockkObject | เปลี่ยนวัตถุให้กลายเป็นวัตถุจำลองหรือล้างหากถูกเปลี่ยนไปแล้ว |
unmockkObject | เปลี่ยนวัตถุจำลองกลับเป็นวัตถุปกติ |
mockkStatic | ทำให้การเยาะเย้ยแบบคงที่ออกจากชั้นเรียนหรือล้างมันถ้ามันเปลี่ยนไปแล้ว |
unmockkStatic | เปลี่ยนการจำลองแบบคงที่กลับสู่ชั้นเรียนปกติ |
clearStaticMockk | ล้างการจำลองแบบคงที่ |
mockkConstructor | ทำให้ตัวสร้างจำลองออกจากชั้นเรียนหรือล้างมันถ้ามันเปลี่ยนไปแล้ว |
unmockkConstructor | เปลี่ยนตัวสร้างจำลองกลับไปเป็นชั้นเรียนปกติ |
clearConstructorMockk | ล้างการจำลองคอนสตรัคเตอร์ |
unmockkAll | unmocks วัตถุแบบคงที่และตัวสร้าง |
clearAllMocks | เคลียร์การจำลองแบบปกติวัตถุคงที่และคอนสตรัคเตอร์ |
โดยค่าเริ่มต้นอาร์กิวเมนต์ง่าย ๆ จะถูกจับคู่โดยใช้ eq()
| ผู้จับคู่ | คำอธิบาย |
|---|---|
any() | ตรงกับอาร์กิวเมนต์ใด ๆ |
any(Class) | ตรงกับอาร์กิวเมนต์ใด ๆ ของคลาสให้ (สำหรับการเยาะเย้ยสะท้อนแสง) |
allAny() | ตัวจับคู่พิเศษที่ใช้ any() แทน eq() สำหรับตัวจับคู่ที่ให้เป็นอาร์กิวเมนต์ง่ายๆ |
isNull() | ตรวจสอบว่าค่าเป็นโมฆะหรือไม่ |
isNull(inverse=true) | ตรวจสอบว่าค่าไม่เป็นโมฆะ |
ofType(type) | ตรวจสอบว่าค่าเป็นของประเภทหรือไม่ |
match { it.startsWith("string") } | จับคู่ผ่านภาคแสดงที่ผ่าน |
coMatch { it.startsWith("string") } | จับคู่ผ่านภาคแสดงของ Coroutine ที่ผ่าน |
matchNullable { it?.startsWith("string") } | จับคู่ค่าที่เป็นโมฆะผ่านเพรดิเคตที่ผ่าน |
coMatchNullable { it?.startsWith("string") } | จับคู่ค่าที่เป็นโมฆะผ่าน Passed Coroutine Predicate |
eq(value) | จับคู่หากค่าเท่ากับค่าที่ให้ผ่านฟังก์ชัน deepEquals |
eq(value, inverse=true) | จับคู่หากค่าไม่เท่ากับค่าที่ให้ผ่านฟังก์ชัน deepEquals |
neq(value) | จับคู่หากค่าไม่เท่ากับค่าที่ให้ผ่านฟังก์ชัน deepEquals |
refEq(value) | จับคู่หากค่าเท่ากับค่าที่ให้ผ่านการเปรียบเทียบการอ้างอิง |
refEq(value, inverse=true) | จับคู่หากค่าไม่เท่ากับค่าที่ให้ผ่านการเปรียบเทียบการอ้างอิง |
nrefEq(value) | จับคู่หากค่าไม่เท่ากับค่าที่ให้ผ่านการเปรียบเทียบการอ้างอิง |
cmpEq(value) | จับคู่หากค่าเท่ากับค่าที่ให้ผ่านฟังก์ชัน compareTo |
less(value) | จับคู่หากค่าน้อยกว่าค่าที่ให้ไว้ผ่านฟังก์ชัน compareTo |
more(value) | จับคู่หากค่าเป็นมากกว่าค่าที่ให้ผ่านฟังก์ชัน compareTo |
less(value, andEquals=true) | จับคู่หากค่าน้อยกว่าหรือเท่ากับค่าที่ให้ไว้ผ่านฟังก์ชัน compareTo |
more(value, andEquals=true) | จับคู่หากค่ามากกว่าหรือเท่ากับค่าที่ให้ไว้ผ่านฟังก์ชัน compareTo |
range(from, to, fromInclusive=true, toInclusive=true) | จับคู่หากค่าอยู่ในช่วงผ่านฟังก์ชัน compareTo |
and(left, right) | รวมสองจับคู่ผ่านตรรกะและ |
or(left, right) | รวมสองจับคู่ผ่านตรรกะหรือ |
not(matcher) | คัดค้านตัวจับคู่ |
capture(slot) | จับค่าที่ไม่เป็นโมฆะไปยัง CapturingSlot |
captureNullable(slot) | จับค่าที่เป็นโมฆะให้กับ CapturingSlot |
capture(mutableList) | รวบรวมค่าไปยังรายการ |
captureNullable(mutableList) | รวบรวมค่าไปยังรายการพร้อมกับค่า NULL |
captureLambda() | จับแลมบ์ดา |
captureCoroutine() | จับ coroutine |
invoke(...) | เรียกอาร์กิวเมนต์ที่ตรงกัน |
coInvoke(...) | เรียกอาร์กิวเมนต์ที่ตรงกันสำหรับ coroutine |
hint(cls) | คำแนะนำประเภทผลตอบแทนถัดไปในกรณีที่ถูกลบออกไป |
anyVararg() | ตรงกับองค์ประกอบใด ๆ ใน vararg |
varargAny(matcher) | จับคู่หากองค์ประกอบใด ๆ ตรงกับตัวจับคู่ |
varargAll(matcher) | จับคู่หากองค์ประกอบทั้งหมดตรงกับตัวจับคู่ |
any...Vararg() | จับคู่องค์ประกอบใด ๆ ใน Vararg (เฉพาะกับประเภทดั้งเดิม) |
varargAny...(matcher) | จับคู่หากองค์ประกอบใด ๆ ตรงกับตัวจับคู่ (เฉพาะกับประเภทดั้งเดิม) |
varargAll...(matcher) | จับคู่หากองค์ประกอบทั้งหมดตรงกับตัวจับคู่ (เฉพาะกับประเภทดั้งเดิม) |
มีการจับคู่พิเศษบางอย่างในโหมดการตรวจสอบเท่านั้น:
| ผู้จับคู่ | คำอธิบาย |
|---|---|
withArg { code } | ตรงกับค่าใด ๆ และอนุญาตให้เรียกใช้รหัสบางส่วน |
withNullableArg { code } | ตรงกับค่าที่ไม่มีค่าใช้จ่ายใด ๆ และอนุญาตให้เรียกใช้รหัสบางส่วน |
coWithArg { code } | ตรงกับค่าใด ๆ และอนุญาตให้เรียกใช้รหัส coroutine บางอย่าง |
coWithNullableArg { code } | ตรงกับค่าที่ไม่มีค่าใช้จ่ายใด ๆ และอนุญาตให้เรียกใช้รหัส coroutine บางตัว |
| ผู้ตรวจสอบความถูกต้อง | คำอธิบาย |
|---|---|
verify { mock.call() } | ทำการตรวจสอบที่ไม่ได้เรียงลำดับว่ามีการโทร |
verify(inverse=true) { mock.call() } | ทำการตรวจสอบที่ไม่ได้จัดเรียงว่าไม่ได้ทำการโทร |
verify(atLeast=n) { mock.call() } | ทำการตรวจสอบที่ไม่ได้เรียงลำดับว่ามีการโทรอย่างน้อย n ครั้ง |
verify(atMost=n) { mock.call() } | ทำการตรวจสอบที่ไม่ได้เรียงลำดับว่ามีการโทรมา n ครั้ง |
verify(exactly=n) { mock.call() } | ทำการตรวจสอบที่ไม่ได้จัดเรียงว่าการโทรดำเนินการอย่างแน่นอน n ครั้ง |
verifyAll { mock.call1(); mock.call2() } | ทำการตรวจสอบที่ไม่ได้เรียงลำดับว่ามีการเรียกใช้การโทรที่ระบุไว้สำหรับการจำลองที่กล่าวถึง |
verifyOrder { mock.call1(); mock.call2() } | ทำการตรวจสอบว่าลำดับการโทรไปหลังจากนั้นอีกครั้ง |
verifySequence { mock.call1(); mock.call2() } | ทำการตรวจสอบว่ามีเพียงลำดับการโทรที่ระบุไว้สำหรับการเยาะเย้ยที่กล่าวถึง |
verify { mock wasNot Called } | ตรวจสอบว่าไม่เรียกว่าเยาะเย้ย |
verify { listOf(mock1, mock2) wasNot Called } | ตรวจสอบว่ารายการของการเยาะเย้ยไม่ได้เรียก |
คำตอบสามารถติดตามได้ด้วยคำตอบเพิ่มเติมอย่างน้อยหนึ่งคำ
| คำตอบ | คำอธิบาย |
|---|---|
returns value | ระบุว่าการโทรที่ตรงกันจะส่งคืนค่าที่ระบุ |
returnsMany list | ระบุว่าการโทรที่ตรงกันจะส่งคืนค่าจากรายการโดยมีการโทรที่ตามมาส่งคืนองค์ประกอบถัดไป |
returnsArgument(n) | ระบุว่าการโทรที่ตรงกันจะส่งคืนอาร์กิวเมนต์ nth ของการโทรนั้น |
throws ex | ระบุว่าการโทรที่จับคู่นั้นมีข้อยกเว้น |
throwsMany list | ระบุว่าการโทรที่จับคู่จะส่งข้อยกเว้นจากรายการโดยมีการโทรที่ตามมาโยนข้อยกเว้นถัดไป |
answers { code } | ระบุว่าคำตอบการโทรที่จับคู่กับบล็อกโค้ดที่มี answer scope |
coAnswers { code } | ระบุว่าคำตอบการโทรที่ตรงกันด้วยบล็อกรหัส coroutine พร้อม answer scope |
answers answerObj | ระบุว่าคำตอบการโทรที่ตรงกันด้วยวัตถุคำตอบ |
answers { nothing } | ระบุว่าคำตอบการโทรที่ตรงกันนั้นเป็นโมฆะ |
just Runs | ระบุว่าการโทรที่ตรงกันคือการส่งคืนหน่วย (ส่งคืนค่า null) |
just Awaits | ระบุว่าการโทรที่ตรงกันจะไม่ส่งคืน (มีให้ตั้งแต่ v1.13.3) |
propertyType Class | ระบุประเภทของ Accessor ฟิลด์สำรอง |
nullablePropertyType Class | ระบุประเภทของ Accessor ฟิลด์สำรองเป็นประเภทที่ไม่มีค่าใช้จ่าย |
คำตอบต่อไปจะถูกส่งคืนในการโทรแต่ละครั้งและค่าสุดท้ายจะคงอยู่ ดังนั้นนี่จึงคล้ายกับความหมาย returnsMany
| คำตอบเพิ่มเติม | คำอธิบาย |
|---|---|
andThen value | ระบุว่าการโทรที่ตรงกันจะส่งคืนค่าที่ระบุหนึ่งค่า |
andThenMany list | ระบุว่าการโทรที่ตรงกันจะส่งคืนค่าจากรายการโดยมีการโทรที่ตามมาส่งคืนองค์ประกอบถัดไป |
andThenThrows ex | ระบุว่าการโทรที่จับคู่นั้นมีข้อยกเว้น |
andThenThrowsMany ex | ระบุว่าการโทรที่จับคู่จะส่งข้อยกเว้นจากรายการโดยมีการโทรที่ตามมาโยนข้อยกเว้นถัดไป |
andThen { code } | ระบุว่าคำตอบการโทรที่จับคู่กับบล็อกโค้ดที่มี answer scope |
coAndThen { code } | ระบุว่าคำตอบการโทรที่ตรงกันด้วยบล็อกรหัส coroutine พร้อม answer scope |
andThenAnswer answerObj | ระบุว่าคำตอบการโทรที่ตรงกันด้วยวัตถุคำตอบ |
andThen { nothing } | ระบุว่าคำตอบการโทรที่ตรงกันนั้นเป็นโมฆะ |
andThenJust Runs | ระบุว่าการโทรที่ตรงกันคือหน่วยส่งคืน (มีให้ตั้งแต่ v1.12.2) |
andThenJust Awaits | ระบุว่าการโทรที่ตรงกันจะไม่กลับมา (มีให้ตั้งแต่ v1.13.3) |
| พารามิเตอร์ | คำอธิบาย |
|---|---|
call | วัตถุการโทรที่ประกอบด้วยการเรียกและตัวจับคู่ |
invocation | มีข้อมูลเกี่ยวกับฟังก์ชั่นจริงที่เรียกใช้ |
matcher | มีข้อมูลเกี่ยวกับตัวจับคู่ที่ใช้เพื่อจับคู่การเรียกร้อง |
self | อ้างอิงถึงการเรียกใช้วัตถุ |
method | อ้างอิงถึงการเรียกใช้ฟังก์ชันที่ทำ |
args | อ้างอิงถึงอาร์กิวเมนต์การเรียกร้อง |
nArgs | จำนวนอาร์กิวเมนต์การเรียกร้อง |
arg(n) | อาร์กิวเมนต์ |
firstArg() | อาร์กิวเมนต์แรก |
secondArg() | อาร์กิวเมนต์ที่สอง |
thirdArg() | อาร์กิวเมนต์ |
lastArg() | อาร์กิวเมนต์สุดท้าย |
captured() | องค์ประกอบสุดท้ายในรายการเพื่อความสะดวกเมื่อจับภาพรายการ |
lambda<...>().invoke() | เรียกแลมบ์ดาที่ถูกจับ |
coroutine<...>().coInvoke() | เรียก coroutine ที่ถูกจับ |
nothing | ค่าว่างสำหรับการส่งคืน nothing เป็นคำตอบ |
fieldValue | อุปกรณ์เสริมไปยังฟิลด์การสำรองคุณสมบัติ |
fieldValueAny | accessor ไปยังฟิลด์การสำรองคุณสมบัติด้วย Any? พิมพ์ |
value | ค่าที่ถูกตั้งค่าให้เลือกประเภทเดียวกับฟิลด์การสำรองคุณสมบัติ |
valueAny | ค่าที่ถูกตั้งค่าด้วย Any? พิมพ์ |
callOriginal | เรียกฟังก์ชันดั้งเดิม |
| พารามิเตอร์ | คำอธิบาย |
|---|---|
position | ตำแหน่งของอาร์กิวเมนต์ในอาเรย์ Vararg |
nArgs | จำนวนอาร์กิวเมนต์โดยรวมในอาร์เรย์ Vararg |
นอกจากนี้คุณยังสามารถสนับสนุนโครงการนี้ได้ด้วยการเป็นสปอนเซอร์ โลโก้ของคุณจะปรากฏขึ้นที่นี่พร้อมลิงค์ไปยังเว็บไซต์ของคุณ
ขอบคุณผู้สนับสนุนของเราทุกคน!
โครงการนี้มีอยู่ขอบคุณทุกคนที่มีส่วนร่วม
หากต้องการถามคำถามโปรดใช้สแต็กล้นหรือ gitter
หากต้องการรายงานข้อบกพร่องโปรดใช้โครงการ GitHub